home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-08-15 | 189.5 KB | 6,183 lines |
- /* Copyright (c) 1994, 1995 Amdahl Corporation.
-
- This file is part of XEmacs.
-
- XEmacs is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- XEmacs is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- for more details.
-
- You should have received a copy of the GNU General Public License
- along with XEmacs; see the file COPYING. If not, write to the Free
- Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* Synched up with: Not in FSF. */
-
- /* This file has been Mule-ized with the exception of the extent-replica
- stuff. */
-
- /* Originally written by some people at Lucid.
- Hacked on by jwz.
- Start/end-open stuff added by John Rose (john.rose@eng.sun.com).
- Rewritten from scratch by Ben Wing <wing@netcom.com>, December 1994. */
-
- /* #### To do:
- Fix map-extent-children?
- */
-
- /* Commentary:
-
- Extents are regions over a buffer, with a start and an end position
- denoting the region of the buffer included in the extent. In
- addition, either end can be closed or open, meaning that the endpoint
- is or is not logically included in the extent. Insertion of a character
- at a closed endpoint causes the character to go inside the extent;
- insertion at an open endpoint causes the character to go outside.
-
- Extent endpoints are stored using memory indices (see insdel.c),
- to minimize the amount of adjusting that needs to be done when
- characters are inserted or deleted.
-
- (Formerly, extent endpoints at the gap could be either before or
- after the gap, depending on the open/closedness of the endpoint.
- The intent of this was to make it so that insertions would
- automatically go inside or out of extents as necessary with no
- further work needing to be done. It didn't work out that way,
- however, and just ended up complexifying and buggifying all the
- rest of the code.)
-
- Extent replica endpoints are stored using buffer positions, although
- byte indices would perhaps be more efficient. This is because
- extent replica objects can be copied from one string to another and
- don't contain a pointer to the string they refer to; it also
- avoids having to do endpoint adjustment on them, because characters
- can never be inserted into or deleted from a string (but can be
- changed using `aset' or `fillarray', which might change the byte
- indices).
-
- #### Extent replicas should be rethunk. I think they're a piece
- of shit and ought to be nuked -- instead, extents should just be able
- to exist over strings just like over buffers. Jamie, who (I think)
- implemented extent replicas in the first place, is understandably
- reluctant to see them go, but so far he hasn't brought up any
- compelling reasons why they need to say. (ben)
-
- Extents are compared using memory indices. There are two orderings
- for extents and both orders are kept current at all times. The normal
- or "display" order is as follows:
-
- Extent A is "less than" extent B, that is, earlier in the display order,
- if: A-start < B-start,
- or if: A-start = B-start, and A-end > B-end
-
- So if two extents begin at the same position, the larger of them is the
- earlier one in the display order (EXTENT_LESS is true).
-
- For the e-order, the same thing holds: Extent A is "less than" extent B
- in e-order, that is, later in the buffer,
- if: A-end < B-end,
- or if: A-end = B-end, and A-start > B-start
-
- So if two extents end at the same position, the smaller of them is the
- earlier one in the e-order (EXTENT_E_LESS is true).
-
- The display order and the e-order are complementary orders: any
- theorem about the display order also applies to the e-order if you
- swap all occurrences of "display order" and "e-order", "less than"
- and "greater than", and "extent start" and "extent end".
-
- Extents can be zero-length, and will end up that way if their endpoints
- are explicitly set that way or if their detachable property is nil
- and all the text in the extent is deleted. (The exception is open-open
- zero-length extents, which are barred from existing because there is
- no sensible way to define their properties. Deletion of the text in
- an open-open extent causes it to be converted into a closed-open
- extent.) Zero-length extents are primarily used to represent
- annotations, and behave as follows:
-
- 1) Insertion at the position of a zero-length extent expands the extent
- if both endpoints are closed; goes after the extent if it is closed-open;
- and goes before the extent if it is open-closed.
-
- 2) Deletion of a character on a side of a zero-length extent whose
- corresponding endpoint is closed causes the extent to be detached if
- it is detachable; if the extent is not detachable or the corresponding
- endpoint is open, the extent remains in the buffer, moving as necessary.
-
- Note that closed-open, non-detachable zero-length extents behave exactly
- like markers and that open-closed, non-detachable zero-length extents
- behave like the "point-type" marker in Mule.
-
-
- #### The following information is wrong in places.
-
- More about the different orders:
- --------------------------------
-
- The extents in a buffer are ordered by "display order" because that
- is that order that the redisplay mechanism needs to process them in.
- The e-order is an auxiliary ordering used to facilitate operations
- over extents. The operations that can be performed on the ordered
- list of extents in a buffer are
-
- 1) Locate where an extent would go if inserted into the list.
- 2) Insert an extent into the list.
- 3) Remove an extent from the list.
- 4) Map over all the extents that overlap a range.
-
- (4) requires being able to determine the first and last extents
- that overlap a range.
-
- First, define >, <, <=, etc. as applied to extents to mean
- comparison according to the display order. Comparison between an
- extent E and an index I means comparison between E and the range
- [I, I].
- Also define e>, e<, e<=, etc. to mean comparison according to the
- e-order.
- For any range R, define R(0) to be the starting index of the range
- and R(1) to be the ending index of the range.
- For any extent E, define E(next) to be the extent directly following
- E, and E(prev) to be the extent directly preceding E. Assume
- E(next) and E(prev) can be determined from E in constant time.
- (This is because we store the extent list as a doubly linked
- list.)
- Similarly, define E(e-next) and E(e-prev) to be the extents
- directly following and preceding E in the e-order.
-
- Now:
-
- Let R be a range.
- Let F be the first extent overlapping R.
- Let L be the last extent overlapping R.
-
- Theorem 1: R(1) lies between L and L(next), i.e. L <= R(1) < L(next).
-
- This follows easily from the definition of display order. The
- basic reason that this theorem applies is that the display order
- sorts by increasing starting index.
-
- Therefore, we can determine L just by looking at where we would
- insert R(1) into the list, and if we know F and are moving forward
- over extents, we can easily determine when we've hit L by comparing
- the extent we're at to R(1).
-
- Theorem 2: F(e-prev) e< [1, R(0)] e<= F.
-
- This is the analog of Theorem 1, and applies because the e-order
- sorts by increasing ending index.
-
- Therefore, F can be found in the same amount of time as operation (1),
- i.e. the time that it takes to locate where an extent would go if
- inserted into the e-order list.
-
- If the lists were stored as balanced binary trees, then operation (1)
- would take logarithmic time, which is usually quite fast. However,
- currently they're stored as simple doubly-linked lists, and instead
- we do some caching to try to speed things up.
-
- Define a "stack of extents" (or "SOE") as the set of extents
- (ordered in the display order) that overlap an index I, together with
- the SOE's "previous" extent, which is an extent that precedes I in
- the e-order. (Hopefully there will not be very many extents between
- I and the previous extent.)
-
- Now:
-
- Let I be an index, let S be the stack of extents on I, let F be
- the first extent in S, and let P be S's previous extent.
-
- Theorem 3: The first extent in S is the first extent that overlaps
- any range [I, J].
-
- Proof: Any extent that overlaps [I, J] but does not include I must
- have a start index > I, and thus be greater than any extent in S.
-
- Therefore, finding the first extent that overlaps a range R is the
- same as finding the first extent that overlaps R(0).
-
- Theorem 4: Let I2 be an index such that I2 > I, and let F2 be the
- first extent that overlaps I2. Then, either F2 is in S or F2 is
- greater than any extent in S.
-
- Proof: If F2 does not include I then its start index is greater
- than I and thus it is greater than any extent in S, including F.
- Otherwise, F2 includes I and thus is in S, and thus F2 >= F.
-
- */
-
- #include <config.h>
- #include "lisp.h"
-
- #include "buffer.h"
- #include "debug.h"
- #include "device.h"
- #include "extents.h"
- #include "faces.h"
- #include "frame.h"
- #include "glyphs.h"
- #include "hash.h"
- #include "insdel.h"
- #include "opaque.h"
- #include "process.h"
- #include "redisplay.h"
-
- /* ------------------------------- */
- /* general macros */
- /* ------------------------------- */
-
- #define MAX_INT ((long) ((1L << VALBITS) - 1))
-
- /* ------------------------------- */
- /* gap array */
- /* ------------------------------- */
-
- /* Note that this object is not extent-specific and should perhaps be
- moved into another file. */
-
- /* Holds a marker that moves as elements in the array are inserted and
- deleted, similar to standard markers. */
-
- typedef struct gap_array_marker
- {
- int pos;
- struct gap_array_marker *next;
- } Gap_Array_Marker;
-
- /* Holds a "gap array", which is an array of elements with a gap located
- in it. Insertions and deletions with a high degree of locality
- are very fast, essentially in constant time. Array positions as
- used and returned in the gap array functions are independent of
- the gap. */
-
- typedef struct gap_array
- {
- char *array;
- int gap;
- int gapsize;
- int numels;
- int elsize;
- Gap_Array_Marker *markers;
- } Gap_Array;
-
- Gap_Array_Marker *gap_array_marker_freelist;
-
- /* Convert a "memory position" (i.e. taking the gap into account) into
- the address of the element at (i.e. after) that position. "Memory
- positions" are only used internally and are of type Memind.
- "Array positions" are used externally and are of type int. */
- #define GAP_ARRAY_MEMEL_ADDR(ga, memel) ((ga)->array + (ga)->elsize*(memel))
-
- /* Number of elements currently in a gap array */
- #define GAP_ARRAY_NUM_ELS(ga) ((ga)->numels)
-
- #define GAP_ARRAY_ARRAY_TO_MEMORY_POS(ga, pos) \
- ((pos) <= (ga)->gap ? (pos) : (pos) + (ga)->gapsize)
-
- #define GAP_ARRAY_MEMORY_TO_ARRAY_POS(ga, pos) \
- ((pos) <= (ga)->gap ? (pos) : (pos) - (ga)->gapsize)
-
- /* Convert an array position into the address of the element at
- (i.e. after) that position. */
- #define GAP_ARRAY_EL_ADDR(ga, pos) ((pos) < (ga)->gap ? \
- GAP_ARRAY_MEMEL_ADDR(ga, pos) : \
- GAP_ARRAY_MEMEL_ADDR(ga, (pos) + (ga)->gapsize))
-
- /* ------------------------------- */
- /* extent list */
- /* ------------------------------- */
-
- typedef struct extent_list_marker
- {
- Gap_Array_Marker *m;
- int endp;
- struct extent_list_marker *next;
- } Extent_List_Marker;
-
- typedef struct extent_list
- {
- Gap_Array *start;
- Gap_Array *end;
- Extent_List_Marker *markers;
- } Extent_List;
-
- Extent_List_Marker *extent_list_marker_freelist;
-
- #define EXTENT_LESS_VALS(e,st,nd) ((extent_start (e) < (st)) || \
- ((extent_start (e) == (st)) && \
- (extent_end (e) > (nd))))
-
- #define EXTENT_EQUAL_VALS(e,st,nd) ((extent_start (e) == (st)) && \
- (extent_end (e) == (nd)))
-
- #define EXTENT_LESS_EQUAL_VALS(e,st,nd) ((extent_start (e) < (st)) || \
- ((extent_start (e) == (st)) && \
- (extent_end (e) >= (nd))))
-
- /* Is extent E1 less than extent E2 in the display order? */
- #define EXTENT_LESS(e1,e2) \
- EXTENT_LESS_VALS (e1, extent_start (e2), extent_end (e2))
-
- /* Is extent E1 equal to extent E2? */
- #define EXTENT_EQUAL(e1,e2) \
- EXTENT_EQUAL_VALS (e1, extent_start (e2), extent_end (e2))
-
- /* Is extent E1 less than or equal to extent E2 in the display order? */
- #define EXTENT_LESS_EQUAL(e1,e2) \
- EXTENT_LESS_EQUAL_VALS (e1, extent_start (e2), extent_end (e2))
-
- #define EXTENT_E_LESS_VALS(e,st,nd) ((extent_end (e) < (nd)) || \
- ((extent_end (e) == (nd)) && \
- (extent_start (e) > (st))))
-
- #define EXTENT_E_LESS_EQUAL_VALS(e,st,nd) ((extent_end (e) < (nd)) || \
- ((extent_end (e) == (nd)) && \
- (extent_start (e) >= (st))))
-
- /* Is extent E1 less than extent E2 in the e-order? */
- #define EXTENT_E_LESS(e1,e2) \
- EXTENT_E_LESS_VALS(e1, extent_start (e2), extent_end (e2))
-
- /* Is extent E1 less than or equal to extent E2 in the e-order? */
- #define EXTENT_E_LESS_EQUAL(e1,e2) \
- EXTENT_E_LESS_EQUAL_VALS (e1, extent_start (e2), extent_end (e2))
-
- #define EXTENT_GAP_ARRAY_AT(ga, pos) (* (EXTENT *) GAP_ARRAY_EL_ADDR(ga, pos))
-
- /* ------------------------------- */
- /* auxiliary extent structure */
- /* ------------------------------- */
-
- struct extent_auxiliary extent_auxiliary_defaults;
-
- MAC_DEFINE (EXTENT, mactemp_ancestor_extent);
- MAC_DEFINE (EXTENT, mactemp_aux_extent);
- MAC_DEFINE (EXTENT, mactemp_plist_extent);
- MAC_DEFINE (EXTENT, mactemp_ensure_extent);
- MAC_DEFINE (EXTENT, mactemp_set_extent);
-
- /* ------------------------------- */
- /* buffer-extent primitives */
- /* ------------------------------- */
-
- typedef struct stack_of_extents
- {
- Extent_List *extents;
- Memind pos;
- } Stack_Of_Extents;
-
- Lisp_Object Vthis_is_a_dead_extent_replica;
-
- /* ------------------------------- */
- /* map-extents */
- /* ------------------------------- */
-
- typedef int Endpoint_Index;
-
- #define memind_to_startind(x, start_open) \
- ((Endpoint_Index) (((x) << 1) + !!(start_open)))
- #define memind_to_endind(x, end_open) \
- ((Endpoint_Index) (((x) << 1) - !!(end_open)))
-
- /* Combination macros */
- #define bytind_to_startind(buf, x, start_open) \
- memind_to_startind (bytind_to_memind (buf, x), start_open)
- #define bytind_to_endind(buf, x, end_open) \
- memind_to_endind (bytind_to_memind (buf, x), end_open)
-
- /* ------------------------------- */
- /* extent-object primitives */
- /* ------------------------------- */
-
- /* These macros generalize many standard buffer-position functions to
- either a buffer or a string. */
-
- /* Converting between Meminds and Bytinds, for an extent object.
- For strings, this is a no-op. For buffers, this resolves
- to the standard memind<->bytind converters. */
-
- #define extent_object_bytind_to_memind(obj, ind) \
- (BUFFERP (obj) ? bytind_to_memind (XBUFFER (obj), ind) : (Memind) ind)
-
- #define extent_object_memind_to_bytind(obj, ind) \
- (BUFFERP (obj) ? memind_to_bytind (XBUFFER (obj), ind) : (Bytind) ind)
-
- /* Converting between Bufpos's and Bytinds, for an extent object.
- For strings, this maps to the bytecount<->charcount converters. */
-
- #define extent_object_bufpos_to_bytind(obj, pos) \
- (BUFFERP (obj) ? bufpos_to_bytind (XBUFFER (obj), pos) : \
- (Bytind) charcount_to_bytecount (string_data (XSTRING (obj)), pos))
-
- #define extent_object_bytind_to_bufpos(obj, ind) \
- (BUFFERP (obj) ? bytind_to_bufpos (XBUFFER (obj), ind) : \
- (Bufpos) bytecount_to_charcount (string_data (XSTRING (obj)), ind))
-
- /* Similar for Bufpos's and Meminds. */
-
- #define extent_object_bufpos_to_memind(obj, pos) \
- (BUFFERP (obj) ? bufpos_to_memind (XBUFFER (obj), pos) : \
- (Memind) charcount_to_bytecount (string_data (XSTRING (obj)), pos))
-
- #define extent_object_memind_to_bufpos(obj, ind) \
- (BUFFERP (obj) ? memind_to_bufpos (XBUFFER (obj), ind) : \
- (Bufpos) bytecount_to_charcount (string_data (XSTRING (obj)), ind))
-
- /* Similar for Bytinds and start/end indices. */
-
- #define extent_object_bytind_to_startind(obj, ind, start_open) \
- memind_to_startind (extent_object_bytind_to_memind (obj, ind), \
- start_open)
-
- #define extent_object_bytind_to_endind(obj, ind, end_open) \
- memind_to_endind (extent_object_bytind_to_memind (obj, ind), \
- end_open)
-
- /* absolute and accessible bounds for a string or buffer.
- For a string, this is always just the beginning and end of the string. */
-
- #define extent_object_accessible_start(obj) \
- (BUFFERP (obj) ? BI_BUF_BEGV (XBUFFER (obj)) : (Bytind) 0)
-
- #define extent_object_absolute_start(obj) \
- (BUFFERP (obj) ? BI_BUF_BEG (XBUFFER (obj)) : (Bytind) 0)
-
- #define extent_object_accessible_limit(obj) \
- (BUFFERP (obj) ? BI_BUF_ZV (XBUFFER (obj)) : \
- (Bytind) string_length (XSTRING (obj)))
-
- #define extent_object_absolute_limit(obj) \
- (BUFFERP (obj) ? BI_BUF_Z (XBUFFER (obj)) : \
- (Bytind) string_length (XSTRING (obj)))
-
- /* ------------------------------- */
- /* Lisp-level functions */
- /* ------------------------------- */
-
- /* flags for decode_extent() */
- #define DE_MUST_HAVE_BUFFER 1
- #define DE_MUST_BE_ATTACHED 2
-
- #ifdef ENERGIZE
- extern void restore_energize_extent_state (EXTENT extent);
- extern struct Energize_Extent_Data *energize_extent_data (EXTENT);
- extern Lisp_Object Qenergize;
- #endif
-
- Lisp_Object Vlast_highlighted_extent;
- int mouse_highlight_priority;
-
- Lisp_Object Qextentp;
- Lisp_Object Qextent_replicap;
- Lisp_Object Qextent_live_p;
- Lisp_Object Qextent_replica_live_p;
-
- Lisp_Object Qend_closed;
- Lisp_Object Qstart_open;
- Lisp_Object Qall_extents_closed;
- Lisp_Object Qall_extents_open;
- Lisp_Object Qall_extents_closed_open;
- Lisp_Object Qall_extents_open_closed;
- Lisp_Object Qstart_in_region;
- Lisp_Object Qend_in_region;
- Lisp_Object Qstart_and_end_in_region;
- Lisp_Object Qstart_or_end_in_region;
- Lisp_Object Qnegate_in_region;
-
- Lisp_Object Qdup_list; /* used in string_dups() / set_string_dups() */
-
- Lisp_Object Qdetached;
- Lisp_Object Qdestroyed;
- Lisp_Object Qbegin_glyph;
- Lisp_Object Qend_glyph;
- Lisp_Object Qstart_open;
- Lisp_Object Qend_open;
- Lisp_Object Qstart_closed;
- Lisp_Object Qend_closed;
- Lisp_Object Qread_only;
- /* Qhighlight defined in general.c */
- Lisp_Object Qunique;
- Lisp_Object Qduplicable;
- Lisp_Object Qinvisible;
- Lisp_Object Qintangible;
- Lisp_Object Qdetachable;
- Lisp_Object Qpriority;
-
- Lisp_Object Qglyph_layout; /* This exists only for backwards compatibility. */
- Lisp_Object Qbegin_glyph_layout, Qend_glyph_layout;
- Lisp_Object Qoutside_margin;
- Lisp_Object Qinside_margin;
- Lisp_Object Qwhitespace;
- /* Qtext defined in general.c */
-
- /* partially used in redisplay */
- Lisp_Object Qglyph_invisible;
-
- Lisp_Object Qcopy_function;
- Lisp_Object Qpaste_function;
-
-
- /************************************************************************/
- /* Generalized gap array */
- /************************************************************************/
-
- /* This generalizes the "array with a gap" model used to store buffer
- characters. This is based on the stuff in insdel.c and should
- probably be merged with it. This is not extent-specific and should
- perhaps be moved into a separate file. */
-
- /* ------------------------------- */
- /* internal functions */
- /* ------------------------------- */
-
- /* Adjust the gap array markers in the range (FROM, TO]. Parallel to
- adjust_markers() in insdel.c. */
-
- static void
- gap_array_adjust_markers (Gap_Array *ga, Memind from,
- Memind to, int amount)
- {
- Gap_Array_Marker *m;
-
- for (m = ga->markers; m; m = m->next)
- m->pos = do_marker_adjustment (m->pos, from, to, amount);
- }
-
- /* Move the gap to array position POS. Parallel to move_gap() in
- insdel.c but somewhat simplified. */
-
- static void
- gap_array_move_gap (Gap_Array *ga, int pos)
- {
- int gap = ga->gap;
- int gapsize = ga->gapsize;
-
- assert (ga->array);
- if (pos < gap)
- {
- memmove (GAP_ARRAY_MEMEL_ADDR (ga, pos + gapsize),
- GAP_ARRAY_MEMEL_ADDR (ga, pos),
- (gap - pos)*ga->elsize);
- gap_array_adjust_markers (ga, (Memind) pos, (Memind) gap,
- gapsize);
- }
- else if (pos > gap)
- {
- memmove (GAP_ARRAY_MEMEL_ADDR (ga, gap),
- GAP_ARRAY_MEMEL_ADDR (ga, gap + gapsize),
- (pos - gap)*ga->elsize);
- gap_array_adjust_markers (ga, (Memind) (gap + gapsize),
- (Memind) (pos + gapsize), - gapsize);
- }
- ga->gap = pos;
- }
-
- /* Make the gap INCREMENT characters longer. Parallel to make_gap() in
- insdel.c. */
-
- static void
- gap_array_make_gap (Gap_Array *ga, int increment)
- {
- char *ptr = ga->array;
- int real_gap_loc;
- int old_gap_size;
-
- /* If we have to get more space, get enough to last a while. We use
- a geometric progession that saves on realloc space. */
- increment += 100 + ga->numels / 8;
-
- ptr = xrealloc (ptr,
- (ga->numels + ga->gapsize + increment)*ga->elsize);
- if (ptr == 0)
- memory_full ();
- ga->array = ptr;
-
- real_gap_loc = ga->gap;
- old_gap_size = ga->gapsize;
-
- /* Call the newly allocated space a gap at the end of the whole space. */
- ga->gap = ga->numels + ga->gapsize;
- ga->gapsize = increment;
-
- /* Move the new gap down to be consecutive with the end of the old one.
- This adjusts the markers properly too. */
- gap_array_move_gap (ga, real_gap_loc + old_gap_size);
-
- /* Now combine the two into one large gap. */
- ga->gapsize += old_gap_size;
- ga->gap = real_gap_loc;
- }
-
- /* ------------------------------- */
- /* external functions */
- /* ------------------------------- */
-
- /* Insert NUMELS elements (pointed to by ELPTR) into the specified
- gap array at POS. */
-
- static void
- gap_array_insert_els (Gap_Array *ga, int pos, void *elptr, int numels)
- {
- assert (pos >= 0 && pos <= ga->numels);
- if (ga->gapsize < numels)
- gap_array_make_gap (ga, numels - ga->gapsize);
- if (pos != ga->gap)
- gap_array_move_gap (ga, pos);
-
- memcpy (GAP_ARRAY_MEMEL_ADDR (ga, ga->gap), (char *) elptr,
- numels*ga->elsize);
- ga->gapsize -= numels;
- ga->gap += numels;
- ga->numels += numels;
- /* This is the equivalent of insert-before-markers.
-
- #### Should only happen if marker is "moves forward at insert" type.
- */
-
- gap_array_adjust_markers (ga, pos - 1, pos, numels);
- }
-
- /* Delete NUMELS elements from the specified gap array, starting at FROM. */
-
- static void
- gap_array_delete_els (Gap_Array *ga, int from, int numdel)
- {
- int to = from + numdel;
- int gapsize = ga->gapsize;
-
- assert (from >= 0);
- assert (numdel >= 0);
- assert (to <= ga->numels);
-
- /* Make sure the gap is somewhere in or next to what we are deleting. */
- if (to < ga->gap)
- gap_array_move_gap (ga, to);
- if (from > ga->gap)
- gap_array_move_gap (ga, from);
-
- /* Relocate all markers pointing into the new, larger gap
- to point at the end of the text before the gap. */
- gap_array_adjust_markers (ga, to + gapsize, to + gapsize,
- - numdel - gapsize);
-
- ga->gapsize += numdel;
- ga->numels -= numdel;
- ga->gap = from;
- }
-
- static Gap_Array_Marker *
- gap_array_make_marker (Gap_Array *ga, int pos)
- {
- Gap_Array_Marker *m;
-
- assert (pos >= 0 && pos <= ga->numels);
- if (gap_array_marker_freelist)
- {
- m = gap_array_marker_freelist;
- gap_array_marker_freelist = gap_array_marker_freelist->next;
- }
- else
- m = (Gap_Array_Marker *) xmalloc (sizeof (*m));
-
- m->pos = GAP_ARRAY_ARRAY_TO_MEMORY_POS (ga, pos);
- m->next = ga->markers;
- ga->markers = m;
- return m;
- }
-
- static void
- gap_array_delete_marker (Gap_Array *ga, Gap_Array_Marker *m)
- {
- Gap_Array_Marker *p, *prev;
-
- for (prev = 0, p = ga->markers; p && p != m; prev = p, p = p->next)
- ;
- assert (p);
- if (prev)
- prev->next = p->next;
- else
- ga->markers = p->next;
- m->next = gap_array_marker_freelist;
- m->pos = 0xDEADBEEF; /* -559038737 as an int */
- gap_array_marker_freelist = m;
- }
-
- static void
- gap_array_delete_all_markers (Gap_Array *ga)
- {
- Gap_Array_Marker *p, *next;
-
- for (p = ga->markers; p; p = next)
- {
- next = p->next;
- p->next = gap_array_marker_freelist;
- p->pos = 0xDEADBEEF; /* -559038737 as an int */
- gap_array_marker_freelist = p;
- }
- }
-
- static void
- gap_array_move_marker (Gap_Array *ga, Gap_Array_Marker *m, int pos)
- {
- assert (pos >= 0 && pos <= ga->numels);
- m->pos = GAP_ARRAY_ARRAY_TO_MEMORY_POS (ga, pos);
- }
-
- #define gap_array_marker_pos(ga, m) \
- GAP_ARRAY_MEMORY_TO_ARRAY_POS (ga, (m)->pos)
-
- static Gap_Array *
- make_gap_array (int elsize)
- {
- Gap_Array *ga = (Gap_Array *) xmalloc (sizeof(*ga));
- memset (ga, 0, sizeof(*ga));
- ga->elsize = elsize;
- return ga;
- }
-
- static void
- free_gap_array (Gap_Array *ga)
- {
- if (ga->array)
- xfree (ga->array);
- gap_array_delete_all_markers (ga);
- xfree (ga);
- }
-
-
- /************************************************************************/
- /* Extent list primitives */
- /************************************************************************/
-
- /* A list of extents is maintained as a double gap array: one gap array
- is ordered by start index (the "display order") and the other is
- ordered by end index (the "e-order"). Note that positions in an
- extent list should logically be conceived of as referring *to*
- a particular extent (as is the norm in programs) rather than
- sitting between two extents. Note also that callers of these
- functions should not be aware of the fact that the extent list is
- implemented as an array, except for the fact that positions are
- integers (this should be generalized to handle integers and linked
- list equally well).
- */
-
- /* Number of elements in an extent list */
- #define extent_list_num_els(el) GAP_ARRAY_NUM_ELS(el->start)
-
- /* Return the position at which EXTENT is located in the specified extent
- list (in the display order if ENDP is 0, in the e-order otherwise).
- If the extent is not found, the position where the extent would
- be inserted is returned. If ENDP is 0, the insertion would go after
- all other equal extents. If ENDP is not 0, the insertion would go
- before all other equal extents. If FOUNDP is not 0, then whether
- the extent was found will get written into it. */
-
- static int
- extent_list_locate (Extent_List *el, EXTENT extent, int endp, int *foundp)
- {
- Gap_Array *ga = endp ? el->end : el->start;
- int left = 0, right = GAP_ARRAY_NUM_ELS (ga);
- int oldfoundpos, foundpos;
- int found;
- EXTENT e;
-
- while (left != right)
- {
- /* RIGHT might not point to a valid extent (i.e. it's at the end
- of the list), so NEWPOS must round down. */
- unsigned int newpos = (left + right) >> 1;
- e = EXTENT_GAP_ARRAY_AT (ga, newpos);
-
- if (endp ? EXTENT_E_LESS (e, extent) : EXTENT_LESS (e, extent))
- left = newpos+1;
- else
- right = newpos;
- }
-
- /* Now we're at the beginning of all equal extents. */
- found = 0;
- oldfoundpos = foundpos = left;
- while (foundpos < GAP_ARRAY_NUM_ELS (ga))
- {
- e = EXTENT_GAP_ARRAY_AT (ga, foundpos);
- if (e == extent)
- {
- found = 1;
- break;
- }
- if (!EXTENT_EQUAL (e, extent))
- break;
- foundpos++;
- }
- if (foundp)
- *foundp = found;
- if (found || !endp)
- return foundpos;
- else
- return oldfoundpos;
- }
-
- /* Return the position of the first extent that begins at or after POS
- (or ends at or after POS, if ENDP is not 0).
-
- An out-of-range value for POS is allowed, and guarantees that the
- position at the beginning or end of the extent list is returned. */
-
- static int
- extent_list_locate_from_pos (Extent_List *el, Memind pos, int endp)
- {
- struct extent fake_extent;
- /*
-
- Note that if we search for [POS, POS], then we get the following:
-
- -- if ENDP is 0, then all extents whose start position is <= POS
- lie before the returned position, and all extents whose start
- position is > POS lie at or after the returned position.
-
- -- if ENDP is not 0, then all extents whose end position is < POS
- lie before the returned position, and all extents whose end
- position is >= POS lie at or after the returned position.
-
- */
- set_extent_start (&fake_extent, endp ? pos : pos-1);
- set_extent_end (&fake_extent, endp ? pos : pos-1);
- return extent_list_locate (el, &fake_extent, endp, 0);
- }
-
- /* Return the extent at POS. */
-
- static EXTENT
- extent_list_at (Extent_List *el, Memind pos, int endp)
- {
- Gap_Array *ga = endp ? el->end : el->start;
-
- assert (pos >= 0 && pos < GAP_ARRAY_NUM_ELS (ga));
- return EXTENT_GAP_ARRAY_AT (ga, pos);
- }
-
- /* Insert an extent into an extent list. */
-
- static void
- extent_list_insert (Extent_List *el, EXTENT extent)
- {
- int pos, foundp;
-
- pos = extent_list_locate (el, extent, 0, &foundp);
- assert (!foundp);
- gap_array_insert_els (el->start, pos, &extent, 1);
- pos = extent_list_locate (el, extent, 1, &foundp);
- assert (!foundp);
- gap_array_insert_els (el->end, pos, &extent, 1);
- }
-
- /* Delete an extent from an extent list. */
-
- static void
- extent_list_delete (Extent_List *el, EXTENT extent)
- {
- int pos, foundp;
-
- pos = extent_list_locate (el, extent, 0, &foundp);
- assert (foundp);
- gap_array_delete_els (el->start, pos, 1);
- pos = extent_list_locate (el, extent, 1, &foundp);
- assert (foundp);
- gap_array_delete_els (el->end, pos, 1);
- }
-
- static Extent_List_Marker *
- extent_list_make_marker (Extent_List *el, int pos, int endp)
- {
- Extent_List_Marker *m;
-
- if (extent_list_marker_freelist)
- {
- m = extent_list_marker_freelist;
- extent_list_marker_freelist = extent_list_marker_freelist->next;
- }
- else
- m = (Extent_List_Marker *) xmalloc (sizeof (*m));
-
- m->m = gap_array_make_marker (endp ? el->end : el->start, pos);
- m->endp = endp;
- m->next = el->markers;
- el->markers = m;
- return m;
- }
-
- #define extent_list_move_marker(el, mkr, pos) \
- gap_array_move_marker((mkr)->endp ? (el)->end : (el)->start, (mkr)->m, pos)
-
- static void
- extent_list_delete_marker (Extent_List *el, Extent_List_Marker *m)
- {
- Extent_List_Marker *p, *prev;
-
- for (prev = 0, p = el->markers; p && p != m; prev = p, p = p->next)
- ;
- assert (p);
- if (prev)
- prev->next = p->next;
- else
- el->markers = p->next;
- m->next = extent_list_marker_freelist;
- extent_list_marker_freelist = m;
- gap_array_delete_marker (m->endp ? el->end : el->start, m->m);
- }
-
- #define extent_list_marker_pos(el, mkr) \
- gap_array_marker_pos ((mkr)->endp ? (el)->end : (el)->start, (mkr)->m)
-
- static Extent_List *
- make_extent_list (void)
- {
- Extent_List *el = (Extent_List *) xmalloc (sizeof(*el));
- el->start = make_gap_array (sizeof(EXTENT));
- el->end = make_gap_array (sizeof(EXTENT));
- el->markers = 0;
- return el;
- }
-
- static void
- free_extent_list (Extent_List *el)
- {
- free_gap_array (el->start);
- free_gap_array (el->end);
- xfree (el);
- }
-
-
- /************************************************************************/
- /* Auxiliary extent structure */
- /************************************************************************/
-
- static Lisp_Object mark_extent_auxiliary (Lisp_Object obj,
- void (*markobj) (Lisp_Object));
- DEFINE_LRECORD_IMPLEMENTATION ("extent-auxiliary", extent_auxiliary,
- mark_extent_auxiliary, 0, 0, 0, 0,
- struct extent_auxiliary);
-
- static Lisp_Object
- mark_extent_auxiliary (Lisp_Object obj, void (*markobj) (Lisp_Object))
- {
- struct extent_auxiliary *data =
- (struct extent_auxiliary *) XEXTENT_AUXILIARY (obj);
- ((markobj) (data->begin_glyph));
- ((markobj) (data->end_glyph));
- ((markobj) (data->parent));
- /* data->children is a list so it should be returned rather
- than recursed on */
- return (data->children);
- }
-
- void
- allocate_extent_aux_struct (EXTENT ext)
- {
- Lisp_Object extent_aux = Qnil;
- struct extent_auxiliary *data =
- alloc_lcrecord (sizeof (struct extent_auxiliary),
- lrecord_extent_auxiliary);
-
- copy_lcrecord (data, &extent_auxiliary_defaults);
- XSETEXTENT_AUXILIARY (extent_aux, data);
- ext->plist = Fcons (extent_aux, ext->plist);
- ext->flags.has_aux = 1;
- }
-
-
- /************************************************************************/
- /* Buffer/string extent primitives */
- /************************************************************************/
-
- /* The functions in this section are the ONLY ones that should know
- about the internal implementation of the extent lists. Other functions
- should only know that there are two orderings on extents, the "display"
- order (sorted by start position, basically) and the e-order (sorted
- by end position, basically), and that certain operations are provided
- to manipulate the list. */
-
- /* ------------------------------- */
- /* basic primitives */
- /* ------------------------------- */
-
- static Lisp_Object
- decode_extent_object (Lisp_Object object)
- {
- if (NILP (object))
- XSETBUFFER (object, current_buffer);
- else
- CHECK_LIVE_BUFFER_OR_STRING (object, 0);
- return object;
- }
-
- static void
- strings_not_supported (void)
- {
- error ("Extents over strings not currently supported");
- }
-
- EXTENT
- extent_ancestor_1 (EXTENT e)
- {
- while (e->flags.has_parent)
- {
- /* There should be no circularities except in case of a logic
- error somewhere in the extent code */
- e = XEXTENT (XEXTENT_AUXILIARY (XCAR (e->plist))->parent);
- }
- return e;
- }
-
- /* Given a string or buffer, return its extent list */
-
- static Extent_List *
- extent_object_extent_list (Lisp_Object object)
- {
- if (STRINGP (object))
- {
- strings_not_supported ();
- return 0;
- }
- else
- {
- assert (BUFFERP (object));
- return XBUFFER (object)->extents;
- }
- }
-
- /* Retrieve the extent list that an extent is a member of */
-
- #define extent_extent_list(e) extent_object_extent_list (extent_object (e))
-
- /* ------------------------------- */
- /* stack of extents */
- /* ------------------------------- */
-
- #ifdef ERROR_CHECK_EXTENTS
-
- void
- sledgehammer_extent_check (Lisp_Object object)
- {
- int i;
- int endp;
- Extent_List *el = extent_object_extent_list (object);
- struct buffer *buf = 0;
-
- if (BUFFERP (object))
- buf = XBUFFER (object);
-
- for (endp = 0; endp < 2; endp++)
- for (i = 1; i < extent_list_num_els (el); i++)
- {
- EXTENT e1 = extent_list_at (el, i-1, endp);
- EXTENT e2 = extent_list_at (el, i, endp);
- if (buf)
- {
- assert (extent_start (e1) <= buf->text.gpt ||
- extent_start (e1) > buf->text.gpt + buf->text.gap_size);
- assert (extent_end (e1) <= buf->text.gpt ||
- extent_end (e1) > buf->text.gpt + buf->text.gap_size);
- }
- assert (extent_start (e1) <= extent_end (e1));
- assert (endp ? (EXTENT_E_LESS_EQUAL (e1, e2)) :
- (EXTENT_LESS_EQUAL (e1, e2)));
- }
- }
-
- #endif
-
- static Stack_Of_Extents *
- extent_object_stack_of_extents (Lisp_Object object)
- {
- if (STRINGP (object))
- {
- /* Maybe not all strings will have a stack of extents. In such
- a case, we need to keep a cache of stacks of extents for the
- strings that don't have them, and return such a cache now.
- Returning 0 is not allowed. */
- strings_not_supported ();
- return 0;
- }
- else
- {
- assert (BUFFERP (object));
- return XBUFFER (object)->soe;
- }
- }
-
- /* #define SOE_DEBUG */
-
- #ifdef SOE_DEBUG
-
- static char *print_extent_1 (char *buf, Lisp_Object extent);
-
- static void
- print_extent_2 (EXTENT e)
- {
- Lisp_Object extent;
- char buf[200];
-
- XSETEXTENT (extent, e);
- print_extent_1 (buf, extent);
- printf ("%s", buf);
- }
-
- static void
- soe_dump (Lisp_Object obj)
- {
- int i;
- Stack_Of_Extents *soe = extent_object_stack_of_extents (obj);
- Extent_List *sel;
- int endp;
-
- sel = soe->extents;
- printf ("SOE pos is %d (memind %d)\n",
- extent_object_memind_to_bytind (obj, soe->pos),
- soe->pos);
- for (endp = 0; endp < 2; endp++)
- {
- printf (endp ? "SOE end:" : "SOE start:");
- for (i = 0; i < extent_list_num_els (sel); i++)
- {
- EXTENT e = extent_list_at (sel, i, endp);
- printf ("\t");
- print_extent_2 (e);
- }
- printf ("\n");
- }
- printf ("\n");
- }
-
- #endif
-
- /* Insert EXTENT into OBJ's stack of extents, if necessary. */
-
- static void
- soe_insert (Lisp_Object obj, EXTENT extent)
- {
- Stack_Of_Extents *soe = extent_object_stack_of_extents (obj);
-
- #ifdef SOE_DEBUG
- printf ("Inserting into SOE: ");
- print_extent_2 (extent);
- printf ("\n");
- #endif
- if (soe->pos < extent_start (extent) || soe->pos > extent_end (extent))
- {
- #ifdef SOE_DEBUG
- printf ("(not needed)\n\n");
- #endif
- return;
- }
- extent_list_insert (soe->extents, extent);
- #ifdef SOE_DEBUG
- printf ("SOE afterwards is:\n");
- soe_dump (obj);
- #endif
- }
-
- /* Delete EXTENT from OBJ's stack of extents, if necessary. */
-
- static void
- soe_delete (Lisp_Object obj, EXTENT extent)
- {
- Stack_Of_Extents *soe = extent_object_stack_of_extents (obj);
-
- #ifdef SOE_DEBUG
- printf ("Deleting from SOE: ");
- print_extent_2 (extent);
- printf ("\n");
- #endif
- if (soe->pos < extent_start (extent) || soe->pos > extent_end (extent))
- {
- #ifdef SOE_DEBUG
- printf ("(not needed)\n\n");
- #endif
- return;
- }
- extent_list_delete (soe->extents, extent);
- #ifdef SOE_DEBUG
- printf ("SOE afterwards is:\n");
- soe_dump (obj);
- #endif
- }
-
- /* Move BUF's stack of extents to lie over the specified position. */
-
- static void
- soe_move (Lisp_Object obj, Memind pos)
- {
- Stack_Of_Extents *soe = extent_object_stack_of_extents (obj);
- Extent_List *sel = soe->extents;
- int numsoe = extent_list_num_els (sel);
- Extent_List *bel = extent_object_extent_list (obj);
- int direction;
- int endp;
-
- #ifdef SOE_DEBUG
- printf ("Moving SOE from %d (memind %d) to %d (memind %d)\n",
- extent_object_memind_to_bytind (obj, soe->pos), soe->pos,
- extent_object_memind_to_bytind (obj, pos), pos);
- #endif
- if (soe->pos < pos)
- {
- direction = 1;
- endp = 0;
- }
- else if (soe->pos > pos)
- {
- direction = -1;
- endp = 1;
- }
- else
- {
- #ifdef SOE_DEBUG
- printf ("(not needed)\n\n");
- #endif
- return;
- }
-
- /* For DIRECTION = 1: Any extent that overlaps POS is either in the
- SOE (if the extent starts at or before SOE->POS) or is greater
- (in the display order) than any extent in the SOE (if it starts
- after SOE->POS).
-
- For DIRECTION = -1: Any extent that overlaps POS is either in the
- SOE (if the extent ends at or after SOE->POS) or is less (in the
- e-order) than any extent in the SOE (if it ends before SOE->POS).
-
- We proceed in two stages:
-
- 1) delete all extents in the SOE that don't overlap POS.
- 2) insert all extents into the SOE that start (or end, when
- DIRECTION = -1) in (SOE->POS, POS] and that overlap
- POS. (Don't include SOE->POS in the range because those
- extents would already be in the SOE.)
- */
-
- /* STAGE 1. */
-
- if (numsoe > 0)
- {
- /* Delete all extents in the SOE that don't overlap POS.
- This is all extents that end before (or start after,
- if DIRECTION = -1) POS.
- */
-
- /* Deleting extents from the SOE is tricky because it changes
- the positions of extents. If we are deleting in the forward
- direction we have to call extent_list_at() on the same position
- over and over again because positions after the deleted element
- get shifted back by 1. To make life simplest, we delete forward
- irrespective of DIRECTION.
- */
- int start, end;
- int i;
-
- if (direction > 0)
- {
- start = 0;
- end = extent_list_locate_from_pos (sel, pos, 1);
- }
- else
- {
- start = extent_list_locate_from_pos (sel, pos+1, 0);
- end = numsoe;
- }
-
- for (i = start; i < end; i++)
- extent_list_delete (sel, extent_list_at (sel, start /* see above */,
- !endp));
- }
-
- /* STAGE 2. */
-
- {
- int start_pos;
-
- if (direction < 0)
- start_pos = extent_list_locate_from_pos (bel, soe->pos, endp) - 1;
- else
- start_pos = extent_list_locate_from_pos (bel, soe->pos + 1, endp);
-
- for (; start_pos >= 0 && start_pos < extent_list_num_els (bel);
- start_pos += direction)
- {
- EXTENT e = extent_list_at (bel, start_pos, endp);
- if ((direction > 0) ?
- (extent_start (e) > pos) :
- (extent_end (e) < pos))
- break; /* All further extents lie on the far side of POS
- and thus can't overlap. */
- if ((direction > 0) ?
- (extent_end (e) >= pos) :
- (extent_start (e) <= pos))
- extent_list_insert (sel, e);
- }
- }
-
- soe->pos = pos;
- #ifdef SOE_DEBUG
- printf ("SOE afterwards is:\n");
- soe_dump (obj);
- #endif
- }
-
- static struct stack_of_extents *
- make_soe (void)
- {
- struct stack_of_extents *soe = (struct stack_of_extents *)
- xmalloc (sizeof (*soe));
- soe->extents = make_extent_list ();
- soe->pos = 0;
- return soe;
- }
-
- static void
- free_soe (struct stack_of_extents *soe)
- {
- free_extent_list (soe->extents);
- xfree (soe);
- }
-
- /* ------------------------------- */
- /* other primitives */
- /* ------------------------------- */
-
- /* Return the start (endp == 0) or end (endp == 1) of an extent as
- a byte index. If you want the value as a memory index, use
- extent_endpoint(). If you want the value as a buffer position,
- use extent_endpoint_bufpos(). */
-
- static Bytind
- extent_endpoint_bytind (EXTENT extent, int endp)
- {
- assert (EXTENT_LIVE_P (extent));
- assert (!extent_detached_p (extent));
- {
- Memind i = (endp) ? (extent_end (extent)) :
- (extent_start (extent));
- Lisp_Object obj = extent_object (extent);
- return extent_object_memind_to_bytind (obj, i);
- }
- }
-
- static Bufpos
- extent_endpoint_bufpos (EXTENT extent, int endp)
- {
- assert (EXTENT_LIVE_P (extent));
- assert (!extent_detached_p (extent));
- {
- Memind i = (endp) ? (extent_end (extent)) :
- (extent_start (extent));
- Lisp_Object obj = extent_object (extent);
- return extent_object_memind_to_bufpos (obj, i);
- }
- }
-
- /* A change to an extent occurred that will change the display, so
- notify redisplay. Maybe also recurse over all the extent's
- descendants. */
-
- static void
- extent_changed_for_redisplay (EXTENT extent, int descendants_too)
- {
- Lisp_Object object;
- struct buffer *b;
- Lisp_Object rest;
-
- /* we could easily encounter a detached extent while traversing the
- children, but we should never be able to encounter a dead extent. */
- assert (EXTENT_LIVE_P (extent));
-
- if (descendants_too)
- {
- /* first mark all of the extent's children. We will lose big-time
- if there are any circularities here, so we sure as hell better
- ensure that there aren't. */
- for (rest = extent_children (extent); !NILP (rest);
- rest = XCDR (rest))
- extent_changed_for_redisplay (XEXTENT (XCAR (rest)), 1);
- }
-
- /* now mark the extent itself. */
-
- object = extent_object (extent);
-
- if (!BUFFERP (object) || extent_detached_p (extent))
- /* #### Can changes to string extents affect redisplay?
- I will have to think about this. What about string glyphs?
- Things in the modeline? etc. */
- return;
-
- b = XBUFFER (object);
- BUF_FACECHANGE (b)++;
- MARK_EXTENTS_CHANGED;
- buffer_extent_signal_changed_region (b,
- extent_endpoint_bufpos (extent, 0),
- extent_endpoint_bufpos (extent, 1));
- }
-
- /* A change to an extent occurred that will might affect redisplay.
- This is called when properties such as the endpoints, the layout,
- or the priority changes. Redisplay will be affected only if
- the extent has any displayable attributes. */
-
- static void
- extent_maybe_changed_for_redisplay (EXTENT extent, int descendants_too)
- {
- EXTENT anc = extent_ancestor (extent);
- if (!NILP (extent_face (anc)) || !NILP (extent_begin_glyph (anc)) ||
- !NILP (extent_end_glyph (anc)) || extent_highlight_p (anc) ||
- extent_invisible_p (anc) || extent_intangible_p (anc))
- extent_changed_for_redisplay (extent, descendants_too);
- }
-
- static EXTENT
- make_extent_detached (Lisp_Object object)
- {
- EXTENT extent = make_extent ();
-
- assert (NILP (object) || STRINGP (object) ||
- (BUFFERP (object) && BUFFER_LIVE_P (XBUFFER (object))));
- extent_object (extent) = object;
- return extent;
- }
-
- static EXTENT
- real_extent_at_forward (Extent_List *el, int pos, int endp)
- {
- for (; pos < extent_list_num_els (el); pos++)
- {
- EXTENT e = extent_list_at (el, pos, endp);
- if (!extent_internal_p (e))
- return e;
- }
- return 0;
- }
-
- static EXTENT
- real_extent_at_backward (Extent_List *el, int pos, int endp)
- {
- for (; pos >= 0; pos--)
- {
- EXTENT e = extent_list_at (el, pos, endp);
- if (!extent_internal_p (e))
- return e;
- }
- return 0;
- }
-
- static EXTENT
- extent_first (Lisp_Object obj)
- {
- return real_extent_at_forward (extent_object_extent_list (obj), 0, 0);
- }
-
- #ifdef DEBUG_XEMACS
- static EXTENT
- extent_e_first (Lisp_Object obj)
- {
- return real_extent_at_forward (extent_object_extent_list (obj), 0, 1);
- }
- #endif
-
- static EXTENT
- extent_next (EXTENT e)
- {
- Extent_List *el = extent_extent_list (e);
- int foundp;
- int pos;
-
- pos = extent_list_locate (el, e, 0, &foundp);
- assert (foundp);
- return real_extent_at_forward (el, pos+1, 0);
- }
-
- #ifdef DEBUG_XEMACS
- static EXTENT
- extent_e_next (EXTENT e)
- {
- Extent_List *el = extent_extent_list (e);
- int foundp;
- int pos;
-
- pos = extent_list_locate (el, e, 1, &foundp);
- assert (foundp);
- return real_extent_at_forward (el, pos+1, 1);
- }
- #endif
-
- static EXTENT
- extent_last (Lisp_Object obj)
- {
- Extent_List *el = extent_object_extent_list (obj);
- return real_extent_at_backward (el, extent_list_num_els (el) - 1, 0);
- }
-
- #ifdef DEBUG_XEMACS
- static EXTENT
- extent_e_last (Lisp_Object obj)
- {
- Extent_List *el = extent_object_extent_list (obj);
- return real_extent_at_backward (el, extent_list_num_els (el) - 1, 1);
- }
- #endif
-
- static EXTENT
- extent_previous (EXTENT e)
- {
- Extent_List *el = extent_extent_list (e);
- int foundp;
- int pos;
-
- pos = extent_list_locate (el, e, 0, &foundp);
- assert (foundp);
- return real_extent_at_backward (el, pos-1, 0);
- }
-
- #ifdef DEBUG_XEMACS
- static EXTENT
- extent_e_previous (EXTENT e)
- {
- Extent_List *el = extent_extent_list (e);
- int foundp;
- int pos;
-
- pos = extent_list_locate (el, e, 1, &foundp);
- assert (foundp);
- return real_extent_at_backward (el, pos-1, 1);
- }
- #endif
-
- static void
- extent_attach (EXTENT extent)
- {
- Extent_List *el = extent_extent_list (extent);
-
- extent_list_insert (el, extent);
- soe_insert (extent_object (extent), extent);
- /* only this extent changed */
- extent_maybe_changed_for_redisplay (extent, 0);
- }
-
- static void
- extent_detach (EXTENT extent)
- {
- Extent_List *el = extent_extent_list (extent);
-
- /* call this before messing with the extent. */
- extent_maybe_changed_for_redisplay (extent, 0);
- extent_list_delete (el, extent);
- soe_delete (extent_object (extent), extent);
- set_extent_start (extent, 0);
- set_extent_end (extent, 0);
- }
-
- /* ------------------------------- */
- /* map-extents et al. */
- /* ------------------------------- */
-
- /* Returns true iff map_extents() would visit the given extent.
- See the comments at map_extents() for info on the overlap rule.
- Assumes that all validation on the extent and buffer positions has
- already been performed (see Fextent_in_region_p ()).
- */
- static int
- extent_in_region_p (EXTENT extent, Bytind from, Bytind to,
- unsigned int flags)
- {
- Lisp_Object obj = extent_object (extent);
- Endpoint_Index start, end, exs, exe;
- int start_open, end_open;
- unsigned int all_extents_flags = flags & ME_ALL_EXTENTS_MASK;
- unsigned int in_region_flags = flags & ME_IN_REGION_MASK;
- int retval;
-
- /* A zero-length region is treated as closed-closed. */
- if (from == to)
- {
- flags |= ME_END_CLOSED;
- flags &= ~ME_START_OPEN;
- }
-
- switch (all_extents_flags)
- {
- case ME_ALL_EXTENTS_CLOSED:
- start_open = end_open = 0; break;
- case ME_ALL_EXTENTS_OPEN:
- start_open = end_open = 1; break;
- case ME_ALL_EXTENTS_CLOSED_OPEN:
- start_open = 0; end_open = 1; break;
- case ME_ALL_EXTENTS_OPEN_CLOSED:
- start_open = 1; end_open = 0; break;
- default:
- start_open = extent_start_open_p (extent);
- end_open = extent_end_open_p (extent);
- break;
- }
-
- /* So is a zero-length extent. */
- if (extent_start (extent) == extent_end (extent))
- start_open = end_open = 0;
-
- start = extent_object_bytind_to_startind (obj, from, flags & ME_START_OPEN);
- end = extent_object_bytind_to_endind (obj, to, ! (flags & ME_END_CLOSED));
- exs = memind_to_startind (extent_start (extent), start_open);
- exe = memind_to_endind (extent_end (extent), end_open);
-
- /* It's easy to determine whether an extent lies *outside* the
- region -- just determine whether it's completely before
- or completely after the region. Reject all such extents, so
- we're now left with only the extents that overlap the region.
- */
-
- if (exs > end || exe < start)
- return 0;
-
- /* See if any further restrictions are called for. */
- switch (in_region_flags)
- {
- case ME_START_IN_REGION:
- retval = start <= exs && exs <= end; break;
- case ME_END_IN_REGION:
- retval = start <= exe && exe <= end; break;
- case ME_START_AND_END_IN_REGION:
- retval = start <= exs && exe <= end; break;
- case ME_START_OR_END_IN_REGION:
- retval = (start <= exs && exs <= end) || (start <= exe && exe <= end);
- break;
- default:
- retval = 1; break;
- }
- return flags & ME_NEGATE_IN_REGION ? !retval : retval;
- }
-
- struct map_extents_struct
- {
- Extent_List *el;
- Extent_List_Marker *mkr;
- EXTENT range;
- };
-
- static Lisp_Object
- map_extents_unwind (Lisp_Object obj)
- {
- struct map_extents_struct *closure =
- (struct map_extents_struct *) get_opaque_ptr (obj);
- if (closure->range)
- extent_detach (closure->range);
- if (closure->mkr)
- extent_list_delete_marker (closure->el, closure->mkr);
- return Qnil;
- }
-
- /* This is the guts of `map-extents' and the other functions that
- map over extents. In theory the operation of this function is
- simple: just figure out what extents we're mapping over, and
- call the function on each one of them in the range. Unfortunately
- there are a wide variety of things that the mapping function
- might do, and we have to be very tricky to avoid getting messed
- up. Furthermore, this function needs to be very fast (it is
- called multiple times every time text is inserted or deleted
- from a buffer), and so we can't always afford the overhead of
- dealing with all the possible things that the mapping function
- might do; thus, there are many flags that can be specified
- indicating what the mapping function might or might not do.
-
- The result of all this is that this is the most complicated
- function in this file. Change it at your own risk!
-
- A potential simplification to the logic below is to determine
- all the extents that the mapping function should be called on
- before any calls are actually made and save them in an array.
- That introduces its own complications, however (the array
- needs to be marked for garbage-collection, and a static array
- cannot be used because map_extents() needs to be reentrant).
- Furthermore, the results might be a little less sensible than
- the logic below. */
-
- static void
- map_extents_bytind (Bytind from, Bytind to, emf fn, void *arg,
- Lisp_Object obj, EXTENT after, unsigned int flags)
- {
- Memind st, en; /* range we're mapping over */
- EXTENT range = 0; /* extent for this, if ME_MIGHT_MODIFY_TEXT */
- Extent_List *el = 0; /* extent list we're iterating over */
- Extent_List_Marker *posm = 0; /* marker for extent list,
- if ME_MIGHT_MODIFY_EXTENTS */
- /* count and struct for unwind-protect, if ME_MIGHT_THROW */
- int count = 0;
- struct map_extents_struct closure;
-
- #ifdef ERROR_CHECK_EXTENTS
- assert (from <= to);
- assert (from >= extent_object_absolute_start (obj) &&
- from <= extent_object_absolute_limit (obj) &&
- to >= extent_object_absolute_start (obj) &&
- to <= extent_object_absolute_limit (obj));
- #endif
-
- if (after)
- {
- assert (EQ (obj, extent_object (after)));
- assert (!extent_detached_p (after));
- }
-
- st = extent_object_bytind_to_memind (obj, from);
- en = extent_object_bytind_to_memind (obj, to);
-
- if (flags & ME_MIGHT_MODIFY_TEXT)
- {
- /* The mapping function might change the text in the buffer,
- so make an internal extent to hold the range we're mapping
- over. */
- range = make_extent_detached (obj);
- set_extent_start (range, st);
- set_extent_end (range, en);
- range->flags.start_open = flags & ME_START_OPEN;
- range->flags.end_open = !(flags & ME_END_CLOSED);
- range->flags.internal = 1;
- range->flags.detachable = 0;
- extent_attach (range);
- }
-
- if (flags & ME_MIGHT_THROW)
- {
- /* The mapping function might throw past us so we need to use an
- unwind_protect() to eliminate the internal extent and range
- that we use. */
- count = specpdl_depth ();
- closure.range = range;
- closure.mkr = 0;
- record_unwind_protect (map_extents_unwind,
- make_opaque_ptr (&closure));
- }
-
- /* ---------- Figure out where we start and what direction
- we move in. This is the trickiest part of this
- function. ---------- */
-
- /* If ME_START_IN_REGION, ME_END_IN_REGION or ME_START_AND_END_IN_REGION
- was specified and ME_NEGATE_IN_REGION was not specified, our job
- is simple because of the presence of the display order and e-order.
- (Note that theoretically do something similar for
- ME_START_OR_END_IN_REGION, but that would require more trickiness
- than it's worth to avoid hitting the same extent twice.)
-
- In the general case, all the extents that overlap a range can be
- divided into two classes: those whose start position lies within
- the range (including the range's end but not including the
- range's start), and those that overlap the start position,
- i.e. those in the SOE for the start position. Or equivalently,
- the extents can be divided into those whose end position lies
- within the range and those in the SOE for the end position. Note
- that for this purpose we treat both the range and all extents in
- the buffer as closed on both ends. If this is not what the ME_
- flags specified, then we've mapped over a few too many extents,
- but no big deal because extent_in_region_p() will filter them
- out. Ideally, we could move the SOE to the closer of the range's
- two ends and work forwards or backwards from there. However, in
- order to make the semantics of the AFTER argument work out, we
- have to always go in the same direction; so we choose to always
- move the SOE to the start position.
-
- When it comes time to do the SOE stage, we first call soe_move()
- so that the SOE gets set up. Note that the SOE might get
- changed while we are mapping over its contents. If we can
- guarantee that the SOE won't get moved to a new position, we
- simply need to put a marker in the SOE and we will track deletions
- and insertions of extents in the SOE. If the SOE might get moved,
- however (this would happen as a result of a recursive invocation
- of map-extents or a call to a redisplay-type function), then
- trying to track its changes is hopeless, so we just keep a
- marker to the first (or last) extent in the SOE and use that as
- our bound.
-
- Finally, if DONT_USE_SOE is defined, we don't use the SOE at all
- and instead just map from the beginning of the buffer. This is
- used for testing purposes and allows the SOE to be calculated
- using map_extents() instead of the other way around. */
-
- {
- int range_flag; /* ME_*_IN_REGION subset of flags */
- int do_soe_stage = 0; /* Are we mapping over the SOE? */
- /* Does the range stage map over start or end positions? */
- int range_endp;
- /* If type == 0, we include the start position in the range stage mapping.
- If type == 1, we exclude the start position in the range stage mapping.
- If type == 2, we begin at range_start_pos, an extent-list position.
- */
- int range_start_type = 0;
- int range_start_pos = 0;
- int stage;
-
- range_flag = flags & ME_IN_REGION_MASK;
- if ((range_flag == ME_START_IN_REGION ||
- range_flag == ME_START_AND_END_IN_REGION) &&
- !(flags & ME_NEGATE_IN_REGION))
- {
- /* map over start position in [range-start, range-end]. No SOE
- stage. */
- range_endp = 0;
- }
- else if (range_flag == ME_END_IN_REGION && !(flags & ME_NEGATE_IN_REGION))
- {
- /* map over end position in [range-start, range-end]. No SOE
- stage. */
- range_endp = 1;
- }
- else
- {
- /* Need to include the SOE extents. */
- #ifdef DONT_USE_SOE
- /* Just brute-force it: start from the beginning. */
- range_endp = 0;
- range_start_type = 2;
- range_start_pos = 0;
- #else
- Stack_Of_Extents *soe = extent_object_stack_of_extents (obj);
- int numsoe;
-
- /* Move the SOE to the closer end of the range. This dictates
- whether we map over start positions or end positions. */
- range_endp = 0;
- soe_move (obj, st);
- numsoe = extent_list_num_els (soe->extents);
- if (numsoe)
- {
- if (flags & ME_MIGHT_MOVE_SOE)
- {
- int foundp;
- /* Can't map over SOE, so just extend range to cover the
- SOE. */
- EXTENT e = extent_list_at (soe->extents, 0, 0);
- range_start_pos =
- extent_list_locate (extent_object_extent_list (obj), e, 0,
- &foundp);
- assert (foundp);
- range_start_type = 2;
- }
- else
- {
- /* We can map over the SOE. */
- do_soe_stage = 1;
- range_start_type = 1;
- }
- }
- else
- {
- /* No extents in the SOE to map over, so we act just as if
- ME_START_IN_REGION or ME_END_IN_REGION was specified.
- RANGE_ENDP already specified so no need to do anything else. */
- }
- }
- #endif
-
- /* ---------- Now loop over the extents. ---------- */
-
- /* We combine the code for the two stages because much of it
- overlaps. */
- for (stage = 0; stage < 2; stage++)
- {
- int pos = 0; /* Position in extent list */
-
- /* First set up start conditions */
- if (stage == 0)
- { /* The SOE stage */
- if (!do_soe_stage)
- continue;
- el = extent_object_stack_of_extents (obj)->extents;
- /* We will always be looping over start extents here. */
- assert (!range_endp);
- pos = 0;
- }
- else
- { /* The range stage */
- el = extent_object_extent_list (obj);
- switch (range_start_type)
- {
- case 0:
- pos = extent_list_locate_from_pos (el, st, range_endp);
- break;
- case 1:
- pos = extent_list_locate_from_pos (el, st + 1, range_endp);
- break;
- case 2:
- pos = range_start_pos;
- break;
- }
- }
-
- if (flags & ME_MIGHT_MODIFY_EXTENTS)
- {
- /* Create a marker to track changes to the extent list */
- if (posm)
- /* Delete the marker used in the SOE stage. */
- extent_list_delete_marker
- (extent_object_stack_of_extents (obj)->extents, posm);
- posm = extent_list_make_marker (el, pos, range_endp);
- /* tell the unwind function about the marker. */
- closure.el = el;
- closure.mkr = posm;
- }
-
- /* Now loop! */
- for (;;)
- {
- EXTENT e;
- Lisp_Object obj2;
-
- /* ----- update position in extent list
- and fetch next extent ----- */
-
- if (posm)
- /* fetch POS again to track extent insertions or deletions */
- pos = extent_list_marker_pos (el, posm);
- if (pos >= extent_list_num_els (el))
- break;
- e = extent_list_at (el, pos, range_endp);
- pos++;
- if (posm)
- /* now point the marker to the next one we're going to process.
- This ensures graceful behavior if this extent is deleted. */
- extent_list_move_marker (el, posm, pos);
-
- /* ----- deal with internal extents ----- */
-
- if (extent_internal_p (e))
- {
- if (!(flags & ME_INCLUDE_INTERNAL))
- continue;
- else if (e == range)
- {
- /* We're processing internal extents and we've
- come across our own special range extent.
- (This happens only in adjust_extents*() and
- process_extents*(), which handle text
- insertion and deletion.) We need to omit
- processing of this extent; otherwise
- we will probably end up prematurely
- terminating this loop. */
- continue;
- }
- }
-
- /* ----- deal with AFTER condition ----- */
-
- if (after)
- {
- /* if e > after, then we can stop skipping extents. */
- if (EXTENT_LESS (after, e))
- after = 0;
- else /* otherwise, skip this extent. */
- continue;
- }
-
- /* ----- stop if we're completely outside the range ----- */
-
- /* fetch ST and EN again to track text insertions or deletions */
- if (range)
- {
- st = extent_start (range);
- en = extent_end (range);
- }
- if (extent_endpoint (e, range_endp) > en)
- {
- /* Can't be mapping over SOE because all extents in
- there should overlap ST */
- assert (stage == 1);
- break;
- }
-
- /* ----- Now actually call the function ----- */
-
- obj2 = extent_object (e);
- if (extent_in_region_p (e,
- extent_object_memind_to_bytind (obj2, st),
- extent_object_memind_to_bytind (obj2, en),
- flags))
- {
- if ((*fn)(e, arg))
- {
- /* Function wants us to stop mapping. */
- stage = 1; /* so outer for loop will terminate */
- break;
- }
- }
- }
- }
- /* ---------- Finished looping. ---------- */
- }
-
- if (flags & ME_MIGHT_THROW)
- /* This deletes the range extent and frees the marker. */
- unbind_to (count, Qnil);
- else
- {
- /* Delete them ourselves */
- if (range)
- extent_detach (range);
- if (posm)
- extent_list_delete_marker (el, posm);
- }
- }
-
- void
- map_extents (Bufpos from, Bufpos to, emf fn, void *arg,
- Lisp_Object obj, EXTENT after, unsigned int flags)
- {
- map_extents_bytind (extent_object_bufpos_to_bytind (obj, from),
- extent_object_bufpos_to_bytind (obj, to), fn, arg, obj,
- after, flags);
- }
-
- /* ------------------------------- */
- /* adjust_extents() */
- /* ------------------------------- */
-
- /* Add AMOUNT to all extent endpoints in the range (FROM, TO]. This
- happens whenever the gap is moved. The reason for this is that
- extent endpoints behave just like markers (all memory indices do)
- and this adjustment correct for markers -- see adjust_markers().
- Note that it is important that we visit all extent endpoints in the
- range, irrespective of whether the endpoints are open or closed.
-
- We could use map_extents() for this (and in fact the function
- was originally written that way), but the gap is in an incoherent
- state when this function is called and this function plays
- around with extent endpoints without detaching and reattaching
- the extents (this is provably correct and saves lots of time),
- so for safety we make it just look at the extent lists directly.
- */
-
- void
- adjust_extents (struct buffer *buf, Memind from, Memind to,
- int amount)
- {
- int endp;
- int pos;
- int startpos[2];
- Lisp_Object obj = Qnil;
- Extent_List *el;
- Stack_Of_Extents *soe;
-
- XSETBUFFER (obj, buf);
- #ifdef ERROR_CHECK_EXTENTS
- sledgehammer_extent_check (obj);
- #endif
- el = extent_object_extent_list (obj);
- /* IMPORTANT! Compute the starting positions of the extents to
- modify BEFORE doing any modification! Otherwise the starting
- position for the second time through the loop might get
- incorrectly calculated (I got bit by this bug real bad). */
- startpos[0] = extent_list_locate_from_pos (el, from+1, 0);
- startpos[1] = extent_list_locate_from_pos (el, from+1, 1);
- for (endp = 0; endp < 2; endp++)
- {
- for (pos = startpos[endp]; pos < extent_list_num_els (el);
- pos++)
- {
- EXTENT e = extent_list_at (el, pos, endp);
- if (extent_endpoint (e, endp) > to)
- break;
- set_extent_endpoint (e,
- do_marker_adjustment (extent_endpoint (e, endp),
- from, to, amount),
- endp);
- }
- }
-
- /* The index for the buffer's SOE is a memory index and thus
- needs to be adjusted like a marker. */
- soe = extent_object_stack_of_extents (obj);
- soe->pos = do_marker_adjustment (soe->pos, from, to, amount);
- }
-
- /* ------------------------------- */
- /* adjust_extents_for_deletion() */
- /* ------------------------------- */
-
- struct adjust_extents_for_deletion_arg
- {
- extent_dynarr *list;
- };
-
- static int
- adjust_extents_for_deletion_mapper (EXTENT extent, void *arg)
- {
- struct adjust_extents_for_deletion_arg *closure =
- (struct adjust_extents_for_deletion_arg *) arg;
-
- Dynarr_add (closure->list, extent);
- return 0; /* continue mapping */
- }
-
- /* For all extent endpoints in the range (FROM, TO], move them to the beginning
- of the new gap. Note that it is important that we visit all extent
- endpoints in the range, irrespective of whether the endpoints are open or
- closed.
- */
-
- void
- adjust_extents_for_deletion (struct buffer *buf, Bytind from,
- Bytind to, int gapsize, int numdel)
- {
- struct adjust_extents_for_deletion_arg closure;
- int i;
- Memind oldsoe, newsoe;
- Lisp_Object bufobj = Qnil;
-
- XSETBUFFER (bufobj, buf);
- #ifdef ERROR_CHECK_EXTENTS
- sledgehammer_extent_check (bufobj);
- #endif
- closure.list = (extent_dynarr *) Dynarr_new (EXTENT);
-
- /* We're going to be playing weird games below with extents and the SOE
- and such, so compute the list now of all the extents that we're going
- to muck with. If we do the mapping and adjusting together, things can
- get all screwed up. */
-
- map_extents_bytind (from, to, adjust_extents_for_deletion_mapper,
- (void *) &closure, bufobj, 0,
- /* extent endpoints move like markers regardless
- of their open/closeness. */
- ME_ALL_EXTENTS_CLOSED | ME_END_CLOSED |
- ME_START_OR_END_IN_REGION | ME_INCLUDE_INTERNAL);
-
- /*
- Old and new values for the SOE's position. (It gets adjusted
- like a marker, just like extent endpoints.)
- */
-
- oldsoe = buf->soe->pos;
- newsoe = do_marker_adjustment (buf->soe->pos,
- (Memind) (to + gapsize),
- (Memind) (to + gapsize),
- - numdel - gapsize);
-
- for (i = 0; i < Dynarr_length (closure.list); i++)
- {
- EXTENT extent = Dynarr_at (closure.list, i);
- Memind new_start, new_end;
-
- /* do_marker_adjustment() will not adjust values that should not be
- adjusted. We're passing the same funky arguments to
- do_marker_adjustment() as buffer_delete_range() does. */
- new_start =
- do_marker_adjustment (extent_start (extent),
- (Memind) (to + gapsize),
- (Memind) (to + gapsize),
- - numdel - gapsize);
- new_end =
- do_marker_adjustment (extent_end (extent),
- (Memind) (to + gapsize),
- (Memind) (to + gapsize),
- - numdel - gapsize);
-
- /* We need to be very careful here so that the SOE doesn't get
- corrupted. We are shrinking extents out of the deleted region
- and simultaneously moving the SOE's pos out of the deleted
- region, so the SOE should contain the same extents at the end
- as at the beginning. However, extents may get reordered
- by this process, so we have to operate by pulling the extents
- out of the buffer and SOE, changing their bounds, and then
- reinserting them. In order for the SOE not to get screwed up,
- we have to make sure that the SOE's pos points to its old
- location whenever we pull an extent out, and points to its
- new location whenever we put the extent back in.
- */
-
- if (new_start != extent_start (extent) ||
- new_end != extent_end (extent))
- {
- extent_detach (extent);
- set_extent_start (extent, new_start);
- set_extent_end (extent, new_end);
- buf->soe->pos = newsoe;
- extent_attach (extent);
- buf->soe->pos = oldsoe;
- }
- }
-
- buf->soe->pos = newsoe;
-
- #ifdef ERROR_CHECK_EXTENTS
- sledgehammer_extent_check (bufobj);
- #endif
- Dynarr_free (closure.list);
- }
-
- /* ------------------------------- */
- /* extent fragments */
- /* ------------------------------- */
-
- /* Imagine that the buffer is divided up into contiguous,
- nonoverlapping "runs" of text such that no extent
- starts or ends within a run (extents that abut the
- run don't count). This function returns the position
- of the beginning of the first run that begins after POS,
- or returns POS if there are no such runs. */
-
- static Bytind
- extent_find_end_of_run (Lisp_Object obj, Bytind pos, int outside_accessible)
- {
- Extent_List *sel = extent_object_stack_of_extents (obj)->extents;
- Extent_List *bel = extent_object_extent_list (obj);
- Bytind pos1, pos2;
- int elind1, elind2;
- Memind mempos = extent_object_bytind_to_memind (obj, pos);
- Bytind limit = outside_accessible ?
- extent_object_absolute_limit (obj) :
- extent_object_accessible_limit (obj);
-
- soe_move (obj, mempos);
-
- /* Find the first start position after POS. */
- elind1 = extent_list_locate_from_pos (bel, mempos+1, 0);
- if (elind1 < extent_list_num_els (bel))
- pos1 = extent_object_memind_to_bytind
- (obj, extent_start (extent_list_at (bel, elind1, 0)));
- else
- pos1 = limit;
-
- /* Find the first end position after POS. The extent corresponding
- to this position is either in the SOE or is greater than or
- equal to POS1, so we just have to look in the SOE. */
- elind2 = extent_list_locate_from_pos (sel, mempos+1, 1);
- if (elind2 < extent_list_num_els (sel))
- pos2 = extent_object_memind_to_bytind
- (obj, extent_end (extent_list_at (sel, elind2, 1)));
- else
- pos2 = limit;
-
- return min (min (pos1, pos2), limit);
- }
-
- static Bytind
- extent_find_beginning_of_run (Lisp_Object obj, Bytind pos,
- int outside_accessible)
- {
- Extent_List *sel = extent_object_stack_of_extents (obj)->extents;
- Extent_List *bel = extent_object_extent_list (obj);
- Bytind pos1, pos2;
- int elind1, elind2;
- Memind mempos = extent_object_bytind_to_memind (obj, pos);
- Bytind limit = outside_accessible ?
- extent_object_absolute_start (obj) :
- extent_object_accessible_start (obj);
-
- soe_move (obj, mempos);
-
- /* Find the first end position before POS. */
- elind1 = extent_list_locate_from_pos (bel, mempos, 1);
- if (elind1 > 0)
- pos1 = extent_object_memind_to_bytind
- (obj,
- extent_end (extent_list_at (bel, elind1 - 1, 1)));
- else
- pos1 = limit;
-
- /* Find the first start position before POS. The extent corresponding
- to this position is either in the SOE or is less than or
- equal to POS1, so we just have to look in the SOE. */
- elind2 = extent_list_locate_from_pos (sel, mempos, 0);
- if (elind2 > 0)
- pos2 = extent_object_memind_to_bytind
- (obj, extent_start (extent_list_at (sel, elind2 - 1, 0)));
- else
- pos2 = limit;
-
- return max (max (pos1, pos2), limit);
- }
-
- struct extent_fragment *
- extent_fragment_new (struct buffer *buf, struct frame *frm)
- {
- struct extent_fragment *ef = (struct extent_fragment *)
- xmalloc (sizeof (struct extent_fragment));
-
- memset (ef, 0, sizeof (*ef));
- ef->buf = buf;
- ef->frm = frm;
- ef->extents = Dynarr_new (EXTENT);
- ef->begin_glyphs = Dynarr_new (struct glyph_block);
- ef->end_glyphs = Dynarr_new (struct glyph_block);
-
- return ef;
- }
-
- void
- extent_fragment_delete (struct extent_fragment *ef)
- {
- Dynarr_free (ef->extents);
- Dynarr_free (ef->begin_glyphs);
- Dynarr_free (ef->end_glyphs);
- xfree (ef);
- }
-
- static int
- extent_priority_sort_function (const void *humpty, const void *dumpty)
- {
- EXTENT foo = * (EXTENT *) humpty;
- EXTENT bar = * (EXTENT *) dumpty;
- if (extent_priority (foo) < extent_priority (bar))
- return -1;
- return (extent_priority (foo) > extent_priority (bar));
- }
-
- static void
- extent_fragment_sort_by_priority (extent_dynarr *extarr)
- {
- int i;
-
- /* Sort our copy of the stack by extent_priority. We use a bubble
- sort here because it's going to be faster than qsort() for small
- numbers of extents (less than 10 or so), and 99.999% of the time
- there won't ever be more extents than this in the stack. */
- if (Dynarr_length (extarr) < 10)
- {
- for (i = 1; i < Dynarr_length (extarr); i++)
- {
- int j = i - 1;
- while (j >= 0 &&
- (extent_priority (Dynarr_at (extarr, j)) >
- extent_priority (Dynarr_at (extarr, j+1))))
- {
- EXTENT tmp = Dynarr_at (extarr, j);
- Dynarr_at (extarr, j) = Dynarr_at (extarr, j+1);
- Dynarr_at (extarr, j+1) = tmp;
- j--;
- }
- }
- }
- else
- /* But some loser programs mess up and may create a large number
- of extents overlapping the same spot. This will result in
- catastrophic behavior if we use the bubble sort above. */
- qsort (Dynarr_atp (extarr, 0), Dynarr_length (extarr),
- sizeof (EXTENT), extent_priority_sort_function);
- }
-
- face_index
- extent_fragment_update (struct window *w, struct extent_fragment *ef,
- Bytind pos)
- {
- int i;
- Extent_List *sel = ef->buf->soe->extents;
- EXTENT lhe = 0;
- struct extent dummy_lhe_extent;
- Memind mempos = bytind_to_memind (ef->buf, pos);
-
- assert (pos >= BI_BUF_BEGV (ef->buf) && pos <= BI_BUF_ZV (ef->buf));
-
- Dynarr_reset (ef->extents);
- Dynarr_reset (ef->begin_glyphs);
- Dynarr_reset (ef->end_glyphs);
- ef->invisible = 0;
-
- /* Set up the begin and end positions. */
- ef->pos = pos;
- ef->end = extent_find_end_of_run (make_buffer (ef->buf), pos, 0);
-
- /* Note that extent_find_end_of_run() already moved the SOE for us. */
- /* soe_move (ef->buf, mempos); */
-
- /* Determine the begin glyphs at POS. */
- for (i = 0; i < extent_list_num_els (sel); i++)
- {
- EXTENT e = extent_list_at (sel, i, 0);
- if (extent_start (e) == mempos && !NILP (extent_begin_glyph (e)))
- {
- Lisp_Object glyph = extent_begin_glyph (e);
- struct glyph_block gb;
-
- gb.glyph = glyph;
- gb.extent = Qnil;
- XSETEXTENT (gb.extent, e);
- Dynarr_add (ef->begin_glyphs, gb);
- }
- }
-
- /* Determine the end glyphs at POS. */
- for (i = 0; i < extent_list_num_els (sel); i++)
- {
- EXTENT e = extent_list_at (sel, i, 1);
- if (extent_end (e) == mempos && !NILP (extent_end_glyph (e)))
- {
- Lisp_Object glyph = extent_end_glyph (e);
- struct glyph_block gb;
-
- gb.glyph = glyph;
- gb.extent = Qnil;
- XSETEXTENT (gb.extent, e);
- Dynarr_add (ef->end_glyphs, gb);
- }
- }
-
- /* Determine whether the last-highlighted-extent is present. */
- if (EXTENTP (Vlast_highlighted_extent))
- lhe = XEXTENT (Vlast_highlighted_extent);
-
- /* Now add all extents that overlap the character after POS and
- have a non-nil face. Also check if the character is invisible. */
- for (i = 0; i < extent_list_num_els (sel); i++)
- {
- EXTENT e = extent_list_at (sel, i, 0);
- if (extent_end (e) > mempos)
- {
- if (extent_invisible_p (e))
- ef->invisible = 1;
- if (!NILP (extent_face (e)) || e == lhe)
- {
- Dynarr_add (ef->extents, e);
- if (e == lhe)
- {
- /* memset isn't really necessary; we only deref `priority' */
- memset (&dummy_lhe_extent, 0, sizeof (dummy_lhe_extent));
- set_extent_priority (&dummy_lhe_extent,
- mouse_highlight_priority);
- Dynarr_add (ef->extents, &dummy_lhe_extent);
- }
- }
- }
- }
-
- extent_fragment_sort_by_priority (ef->extents);
-
- /* Now merge the faces together into a single face. The code to
- do this is in faces.c because it involves manipulating faces. */
- return get_extent_fragment_face_cache_index (w, ef, &dummy_lhe_extent);
- }
-
-
- /************************************************************************/
- /* extent-object methods */
- /************************************************************************/
-
- /* These are the basic helper functions for handling the allocation of
- extent objects and extent-replica objects. They are similar to
- the functions for other lrecord objects. make_extent() is in
- alloc.c, not here. */
-
- static Lisp_Object mark_extent (Lisp_Object, void (*) (Lisp_Object));
- static Lisp_Object mark_extent_replica (Lisp_Object, void (*) (Lisp_Object));
- static int extent_equal (Lisp_Object, Lisp_Object, int depth);
- static int extent_replica_equal (Lisp_Object, Lisp_Object, int depth);
- static unsigned long extent_hash (Lisp_Object obj, int depth);
- static unsigned long extent_replica_hash (Lisp_Object obj, int depth);
- static void print_extent_or_replica (Lisp_Object obj,
- Lisp_Object printcharfun, int escapeflag);
- static int extent_getprop (Lisp_Object obj, Lisp_Object prop,
- Lisp_Object *value_out);
- static int extent_putprop (Lisp_Object obj, Lisp_Object prop,
- Lisp_Object value);
- static int extent_remprop (Lisp_Object obj, Lisp_Object prop);
- static Lisp_Object extent_props (Lisp_Object obj);
-
- DEFINE_LRECORD_IMPLEMENTATION_WITH_PROPS ("extent", extent,
- mark_extent,
- print_extent_or_replica, 0,
- extent_equal, extent_hash,
- extent_getprop, extent_putprop,
- extent_remprop, extent_props,
- struct extent);
- DEFINE_LRECORD_IMPLEMENTATION ("extent-replica", extent_replica,
- mark_extent_replica, print_extent_or_replica,
- 0, extent_replica_equal, extent_replica_hash,
- struct extent_replica);
-
-
- static Lisp_Object
- mark_extent (Lisp_Object obj, void (*markobj) (Lisp_Object))
- {
- struct extent *extent = XEXTENT (obj);
- if (gc_record_type_p (extent_object (extent), lrecord_extent))
- /* Can't be a replica here! */
- abort ();
-
- ((markobj) (extent_object (extent)));
- ((markobj) (extent_face (extent)));
- return (extent->plist);
- }
-
- /* Extents in a buffer are not threaded like normal Lisp_Objects, but
- are stored in an array. Furthermore, the direct pointers are used
- rather than the Lisp_Objects. (This would fail if we had a
- relocating garbage collector, but that is not likely to ever
- happen.) So we have to loop over them ourselves. This function
- is called from mark_buffer(). */
-
- void
- mark_buffer_extents (struct buffer *buf, void (*markobj) (Lisp_Object))
- {
- int i;
- Extent_List *list = buf->extents;
-
- /* Vbuffer_defaults and Vbuffer_local_symbols are buffer-like
- objects that are created specially and never have their extent
- list initialized (or rather, it is set to zero in
- nuke_all_buffer_slots()). However, these objects get
- garbage-collected so we have to deal.
-
- (Also the list can be zero when we're dealing with a destroyed
- buffer.) */
-
- if (!list)
- return;
-
- for (i = 0; i < extent_list_num_els (list); i++)
- {
- struct extent *extent = extent_list_at (list, i, 0);
- Lisp_Object obj = Qnil;
-
- XSETEXTENT (obj, extent);
- ((markobj) (obj));
- ((markobj) (extent->plist));
- ((markobj) (extent_object (extent)));
- }
- }
-
- static Lisp_Object
- mark_extent_replica (Lisp_Object obj, void (*markobj) (Lisp_Object))
- {
- struct extent_replica *dup = XEXTENT_REPLICA (obj);
- if (!gc_record_type_p (extent_replica_extent (dup), lrecord_extent))
- /* Can't be an extent here! */
- abort ();
- return (extent_replica_extent (dup));
- }
-
- static char *
- print_extent_1 (char *buf, Lisp_Object extent_obj)
- {
- Bufpos from = XINT (Fextent_start_position (extent_obj));
- Bufpos to = XINT (Fextent_end_position (extent_obj));
- EXTENT ext = XEXTENT (extent_obj);
- EXTENT anc = extent_ancestor (ext);
- char *bp = buf;
- Lisp_Object tail;
-
- /* Retrieve the ancestor and use it, for faster retrieval of properties */
-
- if (!NILP (extent_begin_glyph (anc))) *bp++ = '*';
- *bp++ = (extent_start_open_p (anc) ? '(': '[');
- if (extent_detached_p (ext))
- sprintf (bp, "detached");
- else
- sprintf (bp, "%d, %d", from, to);
- bp += strlen (bp);
- *bp++ = (extent_end_open_p (anc) ? ')': ']');
- if (!NILP (extent_end_glyph (anc))) *bp++ = '*';
- *bp++ = ' ';
-
- if (extent_read_only_p (anc)) *bp++ = '%';
- if (extent_highlight_p (anc)) *bp++ = 'H';
- if (extent_unique_p (anc)) *bp++ = 'U';
- else if (extent_duplicable_p (anc)) *bp++ = 'D';
- if (extent_invisible_p (anc)) *bp++ = 'I';
-
- if (extent_read_only_p (anc) || extent_highlight_p (anc) ||
- extent_unique_p (anc) || extent_duplicable_p (anc) ||
- extent_invisible_p (anc))
- *bp++ = ' ';
-
- tail = extent_plist (anc);
-
- for (; !NILP (tail); tail = Fcdr (Fcdr (tail)))
- {
- struct Lisp_String *k = XSYMBOL (XCAR (tail))->name;
- Lisp_Object v = XCAR (XCDR (tail));
- if (NILP (v)) continue;
- memcpy (bp, (char *) string_data (k), string_length (k));
- bp += string_length (k);
- *bp++ = ' ';
- }
-
- sprintf (bp, "0x%lx", (long) ext);
- bp += strlen (bp);
-
- *bp++ = 0;
- return buf;
- }
-
- static char *
- print_extent_replica_1 (char *buf, Lisp_Object extent_replica)
- {
- char buf2[256];
-
- Lisp_Object extent =
- extent_replica_extent (XEXTENT_REPLICA (extent_replica));
- if (EXTENT_LIVE_P (XEXTENT (extent)))
- sprintf (buf, "[%d, %d) of extent %s",
- extent_replica_start (XEXTENT_REPLICA (extent_replica)),
- extent_replica_end (XEXTENT_REPLICA (extent_replica)),
- print_extent_1 (buf2, extent));
- else
- sprintf (buf, "[%d, %d) of destroyed extent",
- extent_replica_start (XEXTENT_REPLICA (extent_replica)),
- extent_replica_end (XEXTENT_REPLICA (extent_replica)));
- return buf;
- }
-
- static void
- print_extent_or_replica (Lisp_Object obj,
- Lisp_Object printcharfun, int escapeflag)
- {
- char buf2[256];
-
- if (EXTENTP (obj))
- {
- if (escapeflag)
- {
- CONST char *title = "";
- CONST char *name = "";
- Lisp_Object obj2 = Qnil;
- char stringname[30];
-
- /* Destroyed extents have 't' in the object field, causing
- extent_object() to abort (maybe). */
- if (EXTENT_LIVE_P (XEXTENT (obj)))
- obj2 = extent_object (XEXTENT (obj));
-
- if (NILP (obj2))
- title = "no buffer";
- else if (BUFFERP (obj2))
- {
- if (BUFFER_LIVE_P (XBUFFER (obj2)))
- {
- title = "buffer ";
- name = (char *) string_data (XSTRING (XBUFFER (obj2)->name));
- }
- else
- {
- title = "Killed Buffer";
- name = "";
- }
- }
- else
- {
- assert (STRINGP (obj2));
- title = "string ";
- sprintf (stringname, "0x%x", (unsigned int) XSTRING (obj2));
- }
-
- if (print_readably)
- {
- if (!EXTENT_LIVE_P (XEXTENT (obj)))
- error ("printing unreadable object #<destroyed extent>");
- else
- error ("printing unreadable object #<extent %s>",
- print_extent_1 (buf2, obj));
- }
-
- if (!EXTENT_LIVE_P (XEXTENT (obj)))
- write_c_string ("#<destroyed extent", printcharfun);
- else
- {
- char buf[256];
- write_c_string ("#<extent ", printcharfun);
- if (extent_detached_p (XEXTENT (obj)))
- sprintf (buf, "%s from %s%s",
- print_extent_1 (buf2, obj), title, name);
- else
- sprintf (buf, "%s in %s%s",
- print_extent_1 (buf2, obj),
- title, name);
- write_c_string (buf, printcharfun);
- }
- }
- else
- {
- if (print_readably)
- error ("printing unreadable object #<extent>");
- write_c_string ("#<extent", printcharfun);
- }
- write_c_string (">", printcharfun);
- }
- else if (EXTENT_REPLICAP (obj))
- {
- if (escapeflag)
- {
- if (print_readably)
- {
- if (!EXTENT_REPLICA_LIVE_P (XEXTENT_REPLICA (obj)))
- error
- ("printing unreadable object #<destroyed extent-replica>");
- else
- error ("printing unreadable object #<extent-replica %s>",
- print_extent_replica_1 (buf2, obj));
- }
-
- if (!EXTENT_REPLICA_LIVE_P (XEXTENT_REPLICA (obj)))
- write_c_string ("#<destroyed extent-replica", printcharfun);
- else
- {
- write_c_string ("#<extent-replica ", printcharfun);
- print_extent_replica_1 (buf2, obj);
- write_c_string (buf2, printcharfun);
- }
- }
- else
- {
- if (print_readably)
- error ("printing unreadable object #<extent-replica>");
- write_c_string ("#<extent-replica", printcharfun);
- }
- write_c_string (">", printcharfun);
- }
- }
-
- static int
- properties_equal (EXTENT e1, EXTENT e2, int depth)
- {
- /* When this function is called, all indirections have been followed.
- Thus, the indirection checks in the various macros below will not
- amount to anything, and could be removed. However, the time
- savings would probably not be significant. */
- if (!(EQ (extent_face (e1), extent_face (e2)) &&
- extent_priority (e1) == extent_priority (e2) &&
- internal_equal (extent_begin_glyph (e1), extent_begin_glyph (e2),
- depth + 1) &&
- internal_equal (extent_end_glyph (e1), extent_end_glyph (e2),
- depth + 1)))
- return 0;
-
- /* compare the bit flags. */
- {
- /* The has_aux field should not be relevant. */
- int e1_has_aux = e1->flags.has_aux;
- int e2_has_aux = e2->flags.has_aux;
- int value;
-
- e1->flags.has_aux = e2->flags.has_aux = 0;
- value = memcmp (&e1->flags, &e2->flags, sizeof (e1->flags));
- e1->flags.has_aux = e1_has_aux;
- e2->flags.has_aux = e2_has_aux;
- if (value)
- return 0;
- }
-
- /* compare the random elements of the plists. */
- return (!plists_differ (extent_ancestor_plist (e1),
- extent_ancestor_plist (e2),
- depth + 1));
- }
-
- static int
- extent_equal (Lisp_Object o1, Lisp_Object o2, int depth)
- {
- struct extent *e1 = XEXTENT (o1);
- struct extent *e2 = XEXTENT (o2);
- return
- (extent_start (e1) == extent_start (e2) &&
- extent_end (e1) == extent_end (e2) &&
- internal_equal (extent_object (e1), extent_object (e2), depth + 1) &&
- properties_equal (extent_ancestor (e1), extent_ancestor (e2),
- depth));
- }
-
- static int
- extent_replica_equal (Lisp_Object o1, Lisp_Object o2, int depth)
- {
- struct extent_replica *e1 = XEXTENT_REPLICA (o1);
- struct extent_replica *e2 = XEXTENT_REPLICA (o2);
- if (!EXTENT_REPLICA_LIVE_P (e1) && !EXTENT_REPLICA_LIVE_P (e2))
- return 1;
- return (extent_replica_start (e1) == extent_replica_start (e2) &&
- extent_replica_end (e1) == extent_replica_end (e2) &&
- internal_equal (extent_replica_extent (e1),
- extent_replica_extent (e2), depth + 1));
- }
-
- static unsigned long
- extent_hash (Lisp_Object obj, int depth)
- {
- struct extent *e = XEXTENT (obj);
- /* No need to hash all of the elements; that would take too long.
- Just hash the most common ones. */
- return HASH3 (extent_start (e), extent_end (e),
- internal_hash (extent_object (e), depth + 1));
- }
-
- static unsigned long
- extent_replica_hash (Lisp_Object obj, int depth)
- {
- struct extent_replica *e = XEXTENT_REPLICA (obj);
- if (!EXTENT_REPLICA_LIVE_P (e))
- return 0;
- return HASH3 (extent_replica_start (e), extent_replica_end (e),
- internal_hash (extent_replica_extent (e), depth + 1));
- }
-
- static int
- extent_getprop (Lisp_Object obj, Lisp_Object prop, Lisp_Object *value_out)
- {
- error ("Not yet implemented"); /* #### */
- return 0;
- }
-
- static int
- extent_putprop (Lisp_Object obj, Lisp_Object prop, Lisp_Object value)
- {
- error ("Not yet implemented"); /* #### */
- return 0;
- }
-
- static int
- extent_remprop (Lisp_Object obj, Lisp_Object prop)
- {
- error ("Not yet implemented"); /* #### */
- return 0;
- }
-
- static Lisp_Object
- extent_props (Lisp_Object obj)
- {
- error ("Not yet implemented"); /* #### */
- return Qnil;
- }
-
-
- /************************************************************************/
- /* basic extent accessors */
- /************************************************************************/
-
- /* These functions are for checking externally-passed extent objects
- and returning an extent's basic properties, which include the
- buffer the extent is associated with, the endpoints of the extent's
- range, the open/closed-ness of those endpoints, and whether the
- extent is detached. Manipulating these properties requires
- manipulating the ordered lists that hold extents; thus, functions
- to do that are in a later section. */
-
- /* Given a Lisp_Object that is supposed to be an extent, make sure it
- is OK and return an extent pointer. Extents can be in one of four
- states:
-
- 1) destroyed
- 2) detached and not associated with a buffer
- 3) detached and associated with a buffer
- 4) attached to a buffer
-
- If FLAGS is 0, types 2-4 are allowed. If FLAGS is DE_MUST_HAVE_BUFFER,
- types 3-4 are allowed. If FLAGS is DE_MUST_BE_ATTACHED, only type 4
- is allowed.
- */
-
- static EXTENT
- decode_extent (Lisp_Object extent_obj, unsigned int flags)
- {
- EXTENT extent;
- Lisp_Object obj;
-
- CHECK_LIVE_EXTENT (extent_obj, 0);
- extent = XEXTENT (extent_obj);
- obj = extent_object (extent);
-
- /* the following condition will fail if we're dealing with a freed extent
- or an extent replica */
- assert (NILP (obj) || BUFFERP (obj) || STRINGP (obj));
-
- if (flags & DE_MUST_BE_ATTACHED)
- flags |= DE_MUST_HAVE_BUFFER;
-
- /* if buffer is dead, then convert extent to have no buffer. */
- if (BUFFERP (obj) && !BUFFER_LIVE_P (XBUFFER (obj)))
- obj = extent_object (extent) = Qnil;
-
- assert (!NILP (obj) || extent_detached_p (extent));
-
- if (NILP (obj) && (flags & DE_MUST_HAVE_BUFFER))
- {
- Lisp_Object extent_obj;
- XSETEXTENT (extent_obj, extent);
- signal_simple_error ("extent doesn't belong to a buffer",
- extent_obj);
- }
-
- if (extent_detached_p (extent) && (flags & DE_MUST_BE_ATTACHED))
- {
- Lisp_Object extent_obj;
- XSETEXTENT (extent_obj, extent);
- signal_simple_error ("extent cannot be detached", extent_obj);
- }
-
- return extent;
- }
-
- /* Note that the returned value is a buffer position, not a byte index. */
-
- static Lisp_Object
- extent_endpoint_external (Lisp_Object extent_obj, int endp)
- {
- EXTENT extent = decode_extent (extent_obj, 0);
-
- if (extent_detached_p (extent))
- return Qnil;
- else
- return make_number (extent_endpoint_bufpos (extent, endp));
- }
-
- DEFUN ("extentp", Fextentp, Sextentp, 1, 1, 0,
- "T if OBJECT is an extent.")
- (object)
- Lisp_Object object;
- {
- if (EXTENTP (object))
- return Qt;
- return Qnil;
- }
-
- DEFUN ("extent-live-p", Fextent_live_p, Sextent_live_p, 1, 1, 0,
- "T if OBJECT is an extent and the extent has not been destroyed.")
- (object)
- Lisp_Object object;
- {
- if (EXTENTP (object) && EXTENT_LIVE_P (XEXTENT (object)))
- return Qt;
- return Qnil;
- }
-
- DEFUN ("extent-detached-p", Fextent_detached_p, Sextent_detached_p, 1, 1, 0,
- "T if EXTENT is detached.")
- (extent)
- Lisp_Object extent;
- {
- if (extent_detached_p (decode_extent (extent, 0)))
- return Qt;
- return Qnil;
- }
-
- /* #### This will soon get renamed to `extent-object', with
- extent-buffer being an obsolete alias for it. */
- DEFUN ("extent-buffer", Fextent_object, Sextent_object, 1, 1, 0,
- "Return buffer of EXTENT.")
- (extent)
- Lisp_Object extent;
- {
- return extent_object (decode_extent (extent, 0));
- }
-
- DEFUN ("extent-start-position", Fextent_start_position,
- Sextent_start_position, 1, 1, 0,
- "Return start position of EXTENT, or nil if EXTENT is detached.")
- (extent)
- Lisp_Object extent;
- {
- return extent_endpoint_external (extent, 0);
- }
-
- DEFUN ("extent-end-position", Fextent_end_position,
- Sextent_end_position, 1, 1, 0,
- "Return end position of EXTENT, or nil if EXTENT is detached.")
- (extent)
- Lisp_Object extent;
- {
- return extent_endpoint_external (extent, 1);
- }
-
- DEFUN ("extent-length", Fextent_length, Sextent_length, 1, 1, 0,
- "Return length of EXTENT in characters.")
- (extent)
- Lisp_Object extent;
- {
- EXTENT e = decode_extent (extent, DE_MUST_BE_ATTACHED);
- return
- make_number (extent_endpoint_bufpos (e, 1) -
- extent_endpoint_bufpos (e, 0));
- }
-
- DEFUN ("next-extent", Fnext_extent, Snext_extent, 1, 1, 0,
- "Find next extent after EXTENT.\n\
- If EXTENT is a buffer return the first extent in the buffer.\n\
- Extents in a buffer are ordered in what is called the \"display\"\n\
- order, which sorts by increasing start positions and then by *decreasing*\n\
- end positions.\n\
- If you want to perform an operation on a series of extents, use\n\
- `map-extents' instead of this function; it is much more efficient.\n\
- The primary use of this function should be to enumerate all the\n\
- extents in a buffer.\n\
- Note: The display order is not necessarily the order that `map-extents'\n\
- processes extents in!")
- (extent)
- Lisp_Object extent;
- {
- Lisp_Object val;
- EXTENT next;
-
- if (EXTENTP (extent))
- next = extent_next (decode_extent (extent, DE_MUST_BE_ATTACHED));
- else
- next = extent_first (decode_extent_object (extent));
-
- if (!next)
- return (Qnil);
- XSETEXTENT (val, next);
- return (val);
- }
-
- DEFUN ("previous-extent", Fprevious_extent, Sprevious_extent, 1, 1, 0,
- "Find last extent before EXTENT.\n\
- If EXTENT is a buffer return the last extent in the buffer.\n\
- This function is analogous to `next-extent'.")
- (extent)
- Lisp_Object extent;
- {
- Lisp_Object val;
- EXTENT prev;
-
- if (EXTENTP (extent))
- prev = extent_previous (decode_extent (extent, DE_MUST_BE_ATTACHED));
- else
- prev = extent_last (decode_extent_object (extent));
-
- if (!prev)
- return (Qnil);
- XSETEXTENT (val, prev);
- return (val);
- }
-
- #ifdef DEBUG_XEMACS
-
- DEFUN ("next-e-extent", Fnext_e_extent, Snext_e_extent, 1, 1, 0,
- "Find next extent after EXTENT using the \"e\" order.\n\
- If EXTENT is a buffer, return the first extent in the buffer.")
- (extent)
- Lisp_Object extent;
- {
- Lisp_Object val;
- EXTENT next;
-
- if (EXTENTP (extent))
- next = extent_e_next (decode_extent (extent, DE_MUST_BE_ATTACHED));
- else
- next = extent_e_first (decode_extent_object (extent));
-
- if (!next)
- return (Qnil);
- XSETEXTENT (val, next);
- return (val);
- }
-
- DEFUN ("previous-e-extent", Fprevious_e_extent, Sprevious_e_extent, 1, 1, 0,
- "Find last extent before EXTENT using the \"e\" order.\n\
- If EXTENT is a buffer return the last extent in the buffer.\n\
- This function is analogous to `next-e-extent'.")
- (extent)
- Lisp_Object extent;
- {
- Lisp_Object val;
- EXTENT prev;
-
- if (EXTENTP (extent))
- prev = extent_e_previous (decode_extent (extent, DE_MUST_BE_ATTACHED));
- else
- prev = extent_e_last (decode_extent_object (extent));
-
- if (!prev)
- return (Qnil);
- XSETEXTENT (val, prev);
- return (val);
- }
-
- #endif
-
- DEFUN ("next-extent-change", Fnext_extent_change, Snext_extent_change,
- 1, 2, 0,
- "Return the next position after POS where an extent begins or ends.\n\
- If POS is at the end of the buffer, POS will be returned; otherwise a\n\
- position greater than POS will always be returned.\n\
- If BUFFER is nil, the current buffer is assumed.")
- (pos, buffer)
- Lisp_Object pos, buffer;
- {
- Lisp_Object obj = decode_extent_object (buffer);
- Bytind bpos;
-
- if (BUFFERP (obj))
- bpos = get_bytind (XBUFFER (obj), pos, GB_ALLOW_PAST_ACCESSIBLE);
- else
- {
- assert (STRINGP (obj));
- bpos = get_string_bytepos (obj, pos);
- }
- bpos = extent_find_end_of_run (obj, bpos, 1);
- return make_number (extent_object_bytind_to_bufpos (obj, bpos));
- }
-
- DEFUN ("previous-extent-change", Fprevious_extent_change,
- Sprevious_extent_change, 1, 2, 0,
- "Return the last position before POS where an extent begins or ends.\n\
- If POS is at the beginning of the buffer, POS will be returned; otherwise a\n\
- position less than POS will always be returned.\n\
- If BUFFER is nil, the current buffer is assumed.")
- (pos, buffer)
- Lisp_Object pos, buffer;
- {
- Lisp_Object obj = decode_extent_object (buffer);
- Bytind bpos;
-
- if (BUFFERP (obj))
- bpos = get_bytind (XBUFFER (obj), pos, GB_ALLOW_PAST_ACCESSIBLE);
- else
- {
- assert (STRINGP (obj));
- bpos = get_string_bytepos (obj, pos);
- }
- bpos = extent_find_beginning_of_run (obj, bpos, 1);
- return make_number (extent_object_bytind_to_bufpos (obj, bpos));
- }
-
-
- /************************************************************************/
- /* parent and children stuff */
- /************************************************************************/
-
- DEFUN ("extent-parent", Fextent_parent, Sextent_parent, 1, 1, 0,
- "Return the parent (if any) of EXTENT.\n\
- If an extent has a parent, it derives all its properties from that extent\n\
- and has no properties of its own. It is possible for an extent's parent\n\
- to itself have a parent.")
- (extent)
- Lisp_Object extent;
- /* do I win the prize for the strangest split infinitive? */
- {
- EXTENT e = decode_extent (extent, 0);
- return extent_parent (e);
- }
-
- DEFUN ("extent-children", Fextent_children, Sextent_children, 1, 1, 0,
- "Return a list of the children (if any) of EXTENT.\n\
- The children of an extent are all those extents whose parent is that extent.\n\
- This function does not recursively trace children of children.")
- (extent)
- Lisp_Object extent;
- {
- EXTENT e = decode_extent (extent, 0);
- return Fcopy_sequence (extent_children (e));
- }
-
- static void
- remove_extent_from_children_list (EXTENT e, Lisp_Object child)
- {
- Lisp_Object children = extent_children (e);
- #ifdef ERROR_CHECK_EXTENTS
- assert (!NILP (memq_no_quit (child, children)));
- #endif
- set_extent_ancestor_aux_field (e, children, delq_no_quit (child, children));
- }
-
- static void
- add_extent_to_children_list (EXTENT e, Lisp_Object child)
- {
- Lisp_Object children = extent_children (e);
- #ifdef ERROR_CHECK_EXTENTS
- assert (NILP (memq_no_quit (child, children)));
- #endif
- set_extent_ancestor_aux_field (e, children, Fcons (child, children));
- }
-
- DEFUN ("set-extent-parent", Fset_extent_parent, Sset_extent_parent, 2, 2, 0,
- "Set the parent of EXTENT to PARENT (may be nil).\n\
- See `extent-parent'.")
- (extent, parent)
- Lisp_Object extent, parent;
- {
- EXTENT e = decode_extent (extent, 0);
- Lisp_Object cur_parent = extent_parent (e);
- Lisp_Object rest;
-
- XSETEXTENT (extent, e);
- if (!NILP (parent))
- CHECK_LIVE_EXTENT (parent, 1);
- if (EQ (parent, cur_parent))
- return Qnil;
- for (rest = parent; !NILP (rest); rest = extent_parent (XEXTENT (rest)))
- if (EQ (rest, extent))
- signal_simple_error ("Circular parent chain would result", extent);
- if (NILP (parent))
- {
- remove_extent_from_children_list (XEXTENT (cur_parent), extent);
- set_extent_ancestor_aux_field (e, parent, Qnil);
- e->flags.has_parent = 0;
- }
- else
- {
- add_extent_to_children_list (XEXTENT (parent), extent);
- set_extent_ancestor_aux_field (e, parent, parent);
- e->flags.has_parent = 1;
- }
- /* changing the parent also changes the properties of all children. */
- extent_maybe_changed_for_redisplay (e, 1);
- return Qnil;
- }
-
-
- /************************************************************************/
- /* basic extent mutators */
- /************************************************************************/
-
- /* Note: If you track non-duplicable extents by undo, you'll get bogus
- undo records for transient extents via update-extent.
- For example, query-replace will do this.
- */
-
- static void
- set_extent_endpoints_1 (EXTENT extent, Memind start, Memind end)
- {
- #ifdef ERROR_CHECK_EXTENTS
- Lisp_Object obj = extent_object (extent);
-
- assert (start <= end);
- if (BUFFERP (obj))
- {
- assert (valid_memind_p (XBUFFER (obj), start));
- assert (valid_memind_p (XBUFFER (obj), end));
- }
- #endif
-
- /* Optimization: if the extent is already where we want it to be,
- do nothing. */
- if (!extent_detached_p (extent) && extent_start (extent) == start &&
- extent_end (extent) == end)
- return;
-
- if (extent_detached_p (extent))
- {
- if (extent_duplicable_p (extent))
- {
- Lisp_Object extent_obj;
- XSETEXTENT (extent_obj, extent);
- record_extent (extent_obj, 1);
- }
- }
- else
- extent_detach (extent);
-
- set_extent_start (extent, start);
- set_extent_end (extent, end);
- extent_attach (extent);
- }
-
- static void
- set_extent_endpoints (EXTENT extent, Bytind s, Bytind e)
- {
- Lisp_Object obj = extent_object (extent);
- Memind start, end;
-
- assert (!NILP (obj));
- start = s < 0 ? extent_start (extent) :
- extent_object_bytind_to_memind (obj, s);
- end = e < 0 ? extent_end (extent) :
- extent_object_bytind_to_memind (obj, e);
- set_extent_endpoints_1 (extent, start, end);
- }
-
- static void
- set_extent_openness (EXTENT extent, int start_open, int end_open)
- {
- if (start_open == -1)
- start_open = extent_start_open_p (extent);
- if (end_open == -1)
- end_open = extent_end_open_p (extent);
- extent_start_open_p (extent) = start_open;
- extent_end_open_p (extent) = end_open;
- /* changing the open/closedness of an extent does not affect
- redisplay. */
- }
-
- void
- set_extent_face (EXTENT extent, Lisp_Object face)
- {
- extent = extent_ancestor (extent);
- extent_face (extent) = face;
- extent_changed_for_redisplay (extent, 1);
- }
-
- static void
- set_extent_invisible (EXTENT extent, int flag)
- {
- if (extent_invisible_p (extent) != flag)
- {
- extent_invisible_p (extent) = flag;
- extent_changed_for_redisplay (extent, 1);
- }
- }
-
- static void
- set_extent_intangible (EXTENT extent, int flag)
- {
- if (extent_intangible_p (extent) != flag)
- {
- extent_intangible_p (extent) = flag;
- extent_changed_for_redisplay (extent, 1);
- }
- }
-
- static EXTENT
- make_extent_internal (Lisp_Object object, Bytind from, Bytind to)
- {
- EXTENT extent;
-
- extent = make_extent_detached (object);
- set_extent_endpoints (extent, from, to);
- return extent;
- }
-
- static EXTENT
- copy_extent (EXTENT original, Bytind from, Bytind to, Lisp_Object object)
- {
- EXTENT e;
-
- e = make_extent_detached (object);
- if (from != 0)
- set_extent_endpoints (e, from, to);
-
- e->plist = Fcopy_sequence (original->plist);
- memcpy (&e->flags, &original->flags, sizeof (e->flags));
- if (e->flags.has_aux)
- {
- /* also need to copy the aux struct. It won't work for
- this extent to share the same aux struct as the original
- one. */
- struct extent_auxiliary *data =
- alloc_lcrecord (sizeof (struct extent_auxiliary),
- lrecord_extent_auxiliary);
-
- copy_lcrecord (data, XEXTENT_AUXILIARY (XCAR (original->plist)));
- XSETEXTENT_AUXILIARY (XCAR (e->plist), data);
- }
-
- {
- /* we may have just added another child to the parent extent. */
- Lisp_Object parent = extent_parent (e);
- if (!NILP (parent))
- {
- Lisp_Object extent;
- XSETEXTENT (extent, e);
- add_extent_to_children_list (XEXTENT (parent), extent);
- }
- }
-
- /* #### it's still unclear to me that this Energize-specific junk
- needs to be in here. Just use the general mechanisms, or fix
- them up! --ben */
- #ifdef ENERGIZE
- if (energize_extent_data (original))
- {
- extent_plist (e) = Qnil; /* slightly antisocial... */
- restore_energize_extent_state (e);
- }
- #endif
-
- return e;
- }
-
- Lisp_Object Fset_extent_parent (Lisp_Object, Lisp_Object);
-
- static void
- destroy_extent (EXTENT extent)
- {
- Lisp_Object rest;
- Lisp_Object extent_obj = Qnil;
-
- if (!extent_detached_p (extent))
- extent_detach (extent);
- /* disassociate the extent from its children and parent */
- LIST_LOOP (rest, extent_children (extent))
- Fset_extent_parent (XCAR (rest), Qnil);
- XSETEXTENT (extent_obj, extent);
- Fset_extent_parent (extent_obj, Qnil);
- /* mark the extent as destroyed */
- extent_object (extent) = Qt;
- }
-
- void
- init_buffer_extents (struct buffer *b)
- {
- b->extents = make_extent_list ();
- b->soe = make_soe ();
- }
-
- void
- uninit_buffer_extents (struct buffer *b)
- {
- int i;
-
- free_soe (b->soe);
- b->soe = 0;
- for (i = 0; i < extent_list_num_els (b->extents); i++)
- {
- EXTENT e = extent_list_at (b->extents, i, 0);
- /* No need to do detach_extent(). (Anyway, the SOE has already
- been freed.) Just nuke the damn things. */
- set_extent_start (e, 0);
- set_extent_end (e, 0);
- /* Don't destroy the extent here -- there may still be extent
- replicas pointing to the extent. */
- }
- free_extent_list (b->extents);
- b->extents = 0;
- }
-
- DEFUN ("make-extent", Fmake_extent, Smake_extent, 2, 3, 0,
- "Make an extent for the range [FROM, TO) in BUFFER-OR-STRING.\n\
- BUFFER-OR-STRING defaults to the current buffer. (Note: Currently\n\
- extents over strings are not supported, but will be in the future.)\n\
- Insertions at point TO will be outside of the extent; insertions at\n\
- FROM will be inside the extent, causing the extent to grow. (This is\n\
- the same way that markers behave.) You can change the behavior of\n\
- insertions at the endpoints using `set-extent-property'. The extent is\n\
- initially detached if both FROM and TO are nil, and in this case\n\
- BUFFER-OR-STRING defaults to nil, meaning the extent is in no buffer\n\
- and no string.")
- (from, to, buffer_or_string)
- Lisp_Object from, to, buffer_or_string;
- {
- Lisp_Object extent_obj = Qnil;
- Lisp_Object obj;
-
- if (STRINGP (buffer_or_string))
- strings_not_supported ();
- obj = decode_extent_object (buffer_or_string);
- if (NILP (from) && NILP (to))
- {
- if (NILP (buffer_or_string))
- obj = Qnil;
- XSETEXTENT (extent_obj, make_extent_detached (obj));
- }
- else
- {
- Bytind start, end;
-
- if (STRINGP (obj))
- get_string_range (obj, from, to, &start, &end);
- else
- get_bufrange_bytind (XBUFFER (obj), from, to, &start, &end,
- GB_ALLOW_PAST_ACCESSIBLE);
- XSETEXTENT (extent_obj, make_extent_internal (obj, start, end));
- }
- return extent_obj;
- }
-
- DEFUN ("copy-extent", Fcopy_extent, Scopy_extent, 1, 2, 0,
- "Make a copy of EXTENT. It is initially detached.\n\
- Optional argument BUFFER-OR-STRING defaults to EXTENT's buffer or string.")
- (extent, buffer_or_string)
- Lisp_Object extent, buffer_or_string;
- {
- EXTENT ext = decode_extent (extent, 0);
-
- if (NILP (buffer_or_string))
- buffer_or_string = extent_object (ext);
- else
- buffer_or_string = decode_extent_object (buffer_or_string);
-
- XSETEXTENT (extent, copy_extent (ext, 0, 0, buffer_or_string));
- return extent;
- }
-
- DEFUN ("delete-extent", Fdelete_extent, Sdelete_extent, 1, 1, 0,
- "Remove EXTENT from its buffer and destroy it.\n\
- This does not modify the buffer's text, only its display properties.\n\
- The extent cannot be used thereafter.")
- (extent)
- Lisp_Object extent;
- {
- EXTENT ext;
-
- /* We do not call decode_extent() here because already-destroyed
- extents are OK. */
- CHECK_EXTENT (extent, 0);
- ext = XEXTENT (extent);
-
- if (!EXTENT_LIVE_P (ext))
- return Qnil;
- destroy_extent (ext);
- return Qnil;
- }
-
- DEFUN ("detach-extent", Fdetach_extent, Sdetach_extent, 1, 1, 0,
- "Remove EXTENT from its buffer in such a way that it can be re-inserted.\n\
- An extent is also detached when all of its characters are all killed by a\n\
- deletion, unless its `detachable' property has been unset.\n\
- \n\
- Extents which have the `duplicable' attribute are tracked by the undo\n\
- mechanism. Detachment via `detach-extent' and string deletion is recorded,\n\
- as is attachment via `insert-extent' and string insertion. Extent motion,\n\
- face changes, and attachment via `make-extent' and `set-extent-endpoints'\n\
- are not recorded. This means that extent changes which are to be undo-able\n\
- must be performed by character editing, or by insertion and detachment of\n\
- duplicable extents.")
- (extent)
- Lisp_Object extent;
- {
- EXTENT ext = decode_extent (extent, 0);
-
- if (extent_detached_p (ext))
- return extent;
- if (extent_duplicable_p (ext))
- record_extent (extent, 0);
- extent_detach (ext);
-
- return extent;
- }
-
- DEFUN ("set-extent-endpoints", Fset_extent_endpoints, Sset_extent_endpoints,
- 3, 3, 0,
- "Set the endpoints of EXTENT to START, END.\n\
- If START and END are null, call detach-extent on EXTENT.\n\
- See documentation on `detach-extent' for a discussion of undo recording.")
- (extent, start, end)
- Lisp_Object extent, start, end;
- {
- EXTENT ext;
- Bytind s, e;
- Lisp_Object obj;
-
- if (NILP (start) && NILP (end))
- return Fdetach_extent (extent);
-
- ext = decode_extent (extent, DE_MUST_HAVE_BUFFER);
- obj = extent_object (ext);
- if (STRINGP (obj))
- get_string_range (obj, start, end, &s, &e);
- else
- get_bufrange_bytind (XBUFFER (obj), start, end, &s, &e,
- GB_ALLOW_PAST_ACCESSIBLE);
- set_extent_endpoints (ext, s, e);
- return extent;
- }
-
-
- /************************************************************************/
- /* mapping over extents */
- /************************************************************************/
-
- static unsigned int
- decode_map_extents_flags (Lisp_Object flags)
- {
- unsigned int retval = 0;
- unsigned int all_extents_specified = 0;
- unsigned int in_region_specified = 0;
-
- if (EQ (flags, Qt)) /* obsoleteness compatibility */
- return ME_END_CLOSED;
- if (EQ (flags, Qnil))
- return 0;
- if (SYMBOLP (flags))
- flags = Fcons (flags, Qnil);
- while (!NILP (flags))
- {
- Lisp_Object sym;
- CHECK_CONS (flags, 0);
- sym = XCAR (flags);
- CHECK_SYMBOL (sym, 0);
- if (EQ (sym, Qall_extents_closed) || EQ (sym, Qall_extents_open) ||
- EQ (sym, Qall_extents_closed_open) ||
- EQ (sym, Qall_extents_open_closed))
- {
- if (all_extents_specified)
- error ("Only one `all-extents-*' flag may be specified");
- all_extents_specified = 1;
- }
- if (EQ (sym, Qstart_in_region) || EQ (sym, Qend_in_region) ||
- EQ (sym, Qstart_and_end_in_region) ||
- EQ (sym, Qstart_or_end_in_region))
- {
- if (in_region_specified)
- error ("Only one `*-in-region' flag may be specified");
- in_region_specified = 1;
- }
-
- /* I do so love that conditional operator ... */
- retval |=
- EQ (sym, Qend_closed) ? ME_END_CLOSED :
- EQ (sym, Qstart_open) ? ME_START_OPEN :
- EQ (sym, Qall_extents_closed) ? ME_ALL_EXTENTS_CLOSED :
- EQ (sym, Qall_extents_open) ? ME_ALL_EXTENTS_OPEN :
- EQ (sym, Qall_extents_closed_open) ? ME_ALL_EXTENTS_CLOSED_OPEN :
- EQ (sym, Qall_extents_open_closed) ? ME_ALL_EXTENTS_OPEN_CLOSED :
- EQ (sym, Qstart_in_region) ? ME_START_IN_REGION :
- EQ (sym, Qend_in_region) ? ME_END_IN_REGION :
- EQ (sym, Qstart_and_end_in_region) ? ME_START_AND_END_IN_REGION :
- EQ (sym, Qstart_or_end_in_region) ? ME_START_OR_END_IN_REGION :
- EQ (sym, Qnegate_in_region) ? ME_NEGATE_IN_REGION :
- (signal_simple_error ("Invalid `map-extents' flag", sym), 0);
-
- flags = XCDR (flags);
- }
- return retval;
- }
-
- DEFUN ("extent-in-region-p", Fextent_in_region_p, Sextent_in_region_p, 1, 4, 0,
- "Return whether EXTENT overlaps a specified region.\n\
- This is equivalent to whether `map-extents' would visit EXTENT when called\n\
- with these args.")
- (extent, from, to, flags)
- Lisp_Object extent, from, to, flags;
- {
- EXTENT ext;
- Lisp_Object obj;
- Bytind start, end;
-
- ext = decode_extent (extent, DE_MUST_BE_ATTACHED);
- obj = extent_object (ext);
- if (STRINGP (obj))
- strings_not_supported ();
- get_bufrange_bytind (XBUFFER (obj), from, to, &start, &end, GB_ALLOW_NIL |
- GB_ALLOW_PAST_ACCESSIBLE);
-
- if (extent_in_region_p (ext, start, end, decode_map_extents_flags (flags)))
- return Qt;
- return Qnil;
- }
-
- struct slow_map_extents_arg
- {
- Lisp_Object map_arg;
- Lisp_Object map_routine;
- Lisp_Object result;
- Lisp_Object property;
- Lisp_Object value;
- };
-
- static int
- slow_map_extents_function (EXTENT extent, void *arg)
- {
- /* This function can GC */
- struct slow_map_extents_arg *closure = (struct slow_map_extents_arg *) arg;
- Lisp_Object extent_obj;
-
- XSETEXTENT (extent_obj, extent);
-
- /* make sure this extent qualifies according to the PROPERTY
- and VALUE args */
-
- if (!NILP (closure->property))
- {
- Lisp_Object value = Fextent_property (extent_obj, closure->property);
- if ((NILP (closure->value) && NILP (value)) ||
- (!NILP (closure->value) && !EQ (value, closure->value)))
- return 0;
- }
-
- closure->result = call2 (closure->map_routine, extent_obj,
- closure->map_arg);
- if (NILP (closure->result))
- return 0;
- else
- return 1;
- }
-
- /* This comment supplies the doc string for map-extents.
- for make-docfile to see. We cannot put this in the real DEFUN
- due to limits in the Unix cpp.
-
-
- DEFUN ("map-extents", Fmap_extents, Smap_extents, 1, 8, 0,
- "Map FUNCTION over the extents which overlap a region in BUFFER.\n\
- The region is normally bounded by [FROM, TO) (i.e. the beginning of the\n\
- region is closed and the end of the region is open), but this can be\n\
- changed with the FLAGS argument (see below for a complete discussion).\n\
- \n\
- FUNCTION is called with the arguments (extent, MAPARG). The arguments\n\
- BUFFER, FROM, TO, MAPARG, and CLOSED-END are all optional and default to\n\
- the current buffer, the beginning of BUFFER, the end of BUFFER, nil, and\n\
- nil, respectively. MAP-EXTENTS returns the first non-nil result produced\n\
- by FUNCTION, and no more calls to FUNCTION are made after it returns\n\
- non-nil.\n\
- \n\
- If BUFFER is an extent, FROM and TO default to the extent's endpoints,\n\
- and the mapping omits that extent and its predecessors. This feature\n\
- supports restarting a loop based on `map-extents'.\n\
- \n\
- An extent overlaps the region if there is any point in the extent that is\n\
- also in the region. (For the purpose of overlap, zero-length extents and\n\
- regions are treated as closed on both ends regardless of their endpoints'\n\
- specified open/closedness.) Note that the endpoints of an extent or region\n\
- are considered to be in that extent or region if and only if the\n\
- corresponding end is closed. For example, the extent [5,7] overlaps the\n\
- region [2,5] because 5 is in both the extent and the region. However, (5,7]\n\
- does not overlap [2,5] because 5 is not in the extent, and neither [5,7] nor\n\
- (5,7] overlaps the region [2,5) because 5 is not in the region.\n\
- \n\
- The optional FLAGS can be a symbol or a list of one or more symbols,\n\
- modifying the behavior of `map-extents'. Allow symbols are:\n\
- \n\
- end-closed The region's end is closed.\n\
- \n\
- start-open The region's start is open.\n\
- \n\
- all-extents-closed Treat all extents as closed on both ends for the\n\
- purpose of determining whether they overlap the\n\
- region, irrespective of their actual open- or\n\
- closedness.\n\
- all-extents-open Treat all extents as open on both ends.\n\
- all-extents-closed-open Treat all extents as start-closed, end-open.\n\
- all-extents-open-closed Treat all extents as start-open, end-closed.\n\
- \n\
- start-in-region In addition to the above conditions for extent\n\
- overlap, the extent's start position must lie within\n\
- the specified region. Note that, for this\n\
- condition, open start positions are treated as if\n\
- 0.5 was added to the endpoint's value, and open\n\
- end positions are treated as if 0.5 was subtracted\n\
- from the endpoint's value.\n\
- end-in-region The extent's end position must lie within the
- region.\n\
- start-and-end-in-region Both the extent's start and end positions must lie\n\
- within the region.\n\
- start-or-end-in-region Either the extent's start or end position must lie\n\
- within the region.\n\
- \n\
- negate-in-region The condition specified by a `*-in-region' flag\n\
- must NOT hold for the extent to be considered.\n\
- \n\
- \n\
- At most one of `all-extents-closed', `all-extents-open',\n\
- `all-extents-closed-open', and `all-extents-open-closed' may be specified.\n\
- \n\
- At most one of `start-in-region', `end-in-region',\n\
- `start-and-end-in-region', and `start-or-end-in-region' may be specified.\n\
- \n\
- If optional arg PROPERTY is non-nil, only extents with that property set\n\
- on them will be visited. If optional arg VALUE is non-nil, only extents\n\
- whose value for that property is `eq' to VALUE will be visited.")
- (function, buffer, from, to, maparg, flags, property, value)
-
- */
-
- DEFUN ("map-extents", Fmap_extents, Smap_extents, 1, 8, 0, 0)
- (function, buffer, from, to, maparg, flags, property, value)
- Lisp_Object function, buffer, from, to, maparg, flags, property, value;
- {
- /* This function can GC */
- struct slow_map_extents_arg closure;
- unsigned int me_flags;
- Bytind start, end;
- struct gcpro gcpro1, gcpro2, gcpro3;
- EXTENT after = 0;
- struct buffer *b;
-
- if (EXTENTP (buffer))
- {
- after = decode_extent (buffer, DE_MUST_BE_ATTACHED);
- b = XBUFFER (extent_object (after));
- if (NILP (from)) from = Fextent_start_position (buffer);
- if (NILP (to)) to = Fextent_end_position (buffer);
- }
- else
- b = decode_buffer (buffer, 0);
-
- get_bufrange_bytind (b, from, to, &start, &end, GB_ALLOW_NIL |
- GB_ALLOW_PAST_ACCESSIBLE);
-
- me_flags = decode_map_extents_flags (flags);
-
- if (!NILP (property))
- CHECK_SYMBOL (property, 6);
-
- GCPRO3 (function, maparg, buffer);
-
- closure.map_arg = maparg;
- closure.map_routine = function;
- closure.result = Qnil;
- closure.property = property;
- closure.value = value;
-
- map_extents_bytind (start, end, slow_map_extents_function,
- (void *) &closure, make_buffer (b), after,
- /* You never know what the user might do ... */
- me_flags | ME_MIGHT_CALL_ELISP);
-
- UNGCPRO;
- return closure.result;
- }
-
-
- /************************************************************************/
- /* mapping over extents -- other functions */
- /************************************************************************/
-
- /* ------------------------------- */
- /* map-extent-children */
- /* ------------------------------- */
-
- struct slow_map_extent_children_arg
- {
- Lisp_Object map_arg;
- Lisp_Object map_routine;
- Lisp_Object result;
- Lisp_Object property;
- Lisp_Object value;
- Bytind start_min;
- Bytind prev_start;
- Bytind prev_end;
- };
-
- static int
- slow_map_extent_children_function (EXTENT extent, void *arg)
- {
- /* This function can GC */
- struct slow_map_extent_children_arg *closure =
- (struct slow_map_extent_children_arg *) arg;
- Lisp_Object extent_obj;
- Bytind start = extent_endpoint_bytind (extent, 0);
- Bytind end = extent_endpoint_bytind (extent, 1);
- /* Make sure the extent starts inside the region of interest,
- rather than just overlaps it.
- */
- if (start < closure->start_min)
- return 0;
- /* Make sure the extent is not a child of a previous visited one.
- We know already, because of extent ordering,
- that start >= prev_start, and that if
- start == prev_start, then end <= prev_end.
- */
- if (start == closure->prev_start)
- {
- if (end < closure->prev_end)
- return 0;
- }
- else /* start > prev_start */
- {
- if (start < closure->prev_end)
- return 0;
- /* corner case: prev_end can be -1 if there is no prev */
- }
- XSETEXTENT (extent_obj, extent);
-
- /* make sure this extent qualifies according to the PROPERTY
- and VALUE args */
-
- if (!NILP (closure->property))
- {
- Lisp_Object value = Fextent_property (extent_obj, closure->property);
- if ((NILP (closure->value) && NILP (value)) ||
- (!NILP (closure->value) && !EQ (value, closure->value)))
- return 0;
- }
-
- closure->result = call2 (closure->map_routine, extent_obj,
- closure->map_arg);
-
- /* Since the callback may change the buffer, compute all stored
- buffer positions here.
- */
- closure->start_min = -1; /* no need for this any more */
- closure->prev_start = extent_endpoint_bytind (extent, 0);
- closure->prev_end = extent_endpoint_bytind (extent, 1);
-
- if (NILP (closure->result))
- return 0;
- else
- return 1;
- }
-
- DEFUN ("map-extent-children", Fmap_extent_children, Smap_extent_children,
- 1, 8, 0,
- "Map FUNCTION over the extents in the region from FROM to TO.\n\
- FUNCTION is called with arguments (extent, MAPARG). See `map-extents'\n\
- for a full discussion of the arguments FROM, TO, and FLAGS.\n\
- \n\
- The arguments are the same as for `map-extents', but this function differs\n\
- in that it only visits extents which start in the given region, and also\n\
- in that, after visiting an extent E, it skips all other extents which start\n\
- inside E but end before E's end.\n\
- \n\
- Thus, this function may be used to walk a tree of extents in a buffer:\n\
- (defun walk-extents (buffer &optional ignore)\n\
- (map-extent-children 'walk-extents buffer))")
- (function, buffer, from, to, maparg, flags, property, value)
- Lisp_Object function, buffer, from, to, maparg, flags, property, value;
- {
- /* This function can GC */
- struct slow_map_extent_children_arg closure;
- unsigned int me_flags;
- Bytind start, end;
- struct gcpro gcpro1, gcpro2, gcpro3;
- EXTENT after = 0;
- struct buffer *b;
-
- if (EXTENTP (buffer))
- {
- after = decode_extent (buffer, DE_MUST_BE_ATTACHED);
- b = XBUFFER (extent_object (after));
- if (NILP (from)) from = Fextent_start_position (buffer);
- if (NILP (to)) to = Fextent_end_position (buffer);
- }
- else
- b = decode_buffer (buffer, 0);
-
- get_bufrange_bytind (b, from, to, &start, &end, GB_ALLOW_NIL |
- GB_ALLOW_PAST_ACCESSIBLE);
-
- me_flags = decode_map_extents_flags (flags);
-
- if (!NILP (property))
- CHECK_SYMBOL (property, 6);
-
- GCPRO3 (function, maparg, buffer);
-
- closure.map_arg = maparg;
- closure.map_routine = function;
- closure.result = Qnil;
- closure.property = property;
- closure.value = value;
- closure.start_min = start;
- closure.prev_start = -1;
- closure.prev_end = -1;
- map_extents_bytind (start, end, slow_map_extent_children_function,
- (void *) &closure, make_buffer (b), after,
- /* You never know what the user might do ... */
- me_flags | ME_MIGHT_CALL_ELISP);
-
- UNGCPRO;
- return closure.result;
- }
-
- /* ------------------------------- */
- /* extent-at */
- /* ------------------------------- */
-
- /* find "smallest" matching extent containing pos -- (flag == 0) means
- all extents match, else (EXTENT_FLAGS (extent) & flag) must be true;
- for more than one matching extent with precisely the same endpoints,
- we choose the last extent in the extents_list.
- The search stops just before "before", if that is non-null.
- */
-
- struct extent_at_arg
- {
- EXTENT best_match;
- Memind best_start;
- Memind best_end;
- Lisp_Object prop;
- EXTENT before;
- };
-
- static int
- extent_at_mapper (EXTENT e, void *arg)
- {
- struct extent_at_arg *closure = (struct extent_at_arg *) arg;
-
- if (e == closure->before)
- return 1;
-
- /* If closure->prop is non-nil, then the extent is only acceptable
- if it has a non-nil value for that property. */
- if (!NILP (closure->prop))
- {
- Lisp_Object extent;
- XSETEXTENT (extent, e);
- if (NILP (Fextent_property (extent, closure->prop)))
- return 0;
- }
-
- {
- EXTENT current = closure->best_match;
-
- if (!current)
- goto accept;
- /* redundant but quick test */
- else if (extent_start (current) > extent_start (e))
- return 0;
-
- /* we return the "last" best fit, instead of the first --
- this is because then the glyph closest to two equivalent
- extents corresponds to the "extent-at" the text just past
- that same glyph */
- else if (!EXTENT_LESS_VALS (e, closure->best_start,
- closure->best_end))
- goto accept;
- else
- return 0;
- accept:
- closure->best_match = e;
- closure->best_start = extent_start (e);
- closure->best_end = extent_end (e);
- }
-
- return 0;
- }
-
- DEFUN ("extent-at", Fextent_at, Sextent_at, 1, 4, 0,
- "Find \"smallest\" extent at POS in BUFFER having PROPERTY set.\n\
- An extent is \"at\" POS if it overlaps the region (POS, POS+1); i.e. if\n\
- it covers the character after POS. \"Smallest\" means the extent\n\
- that comes last in the display order; this normally means the extent\n\
- whose start position is closest to POS. See `next-extent' for more\n\
- information.\n\
- BUFFER defaults to the current buffer.\n\
- PROPERTY defaults to nil, meaning that any extent will do.\n\
- Properties are attached to extents with `set-extent-property', which see.\n\
- Returns nil if POS is invalid or there is no matching extent at POS.\n\
- If the fourth argument BEFORE is not nil, it must be an extent; any returned\n\
- extent will precede that extent. This feature allows `extent-at' to be\n\
- used by a loop over extents.")
- (pos, buffer, property, before)
- Lisp_Object pos, buffer, property, before;
- {
- Bytind position;
- Lisp_Object extent_obj = Qnil;
- EXTENT extent;
- struct buffer *buf;
- struct extent_at_arg closure;
-
- buf = decode_buffer (buffer, 0);
- XSETBUFFER (buffer, buf);
- position = get_bytind (buf, pos, GB_NO_ERROR_IF_BAD);
- CHECK_SYMBOL (property, 0);
- if (NILP (before))
- extent = 0;
- else
- extent = decode_extent (before, DE_MUST_BE_ATTACHED);
- if (extent && !EQ (buffer, extent_object (extent)))
- {
- XSETBUFFER (buffer, buf);
- signal_simple_error ("extent not in specified buffer", buffer);
- }
-
- /* it might be argued that invalid positions should cause
- errors, but the principle of least surprise dictates that
- nil should be returned (extent-at is often used in
- response to a mouse event, and in many cases previous events
- have changed the buffer contents). */
- if (!position || position == BI_BUF_Z (buf))
- return Qnil;
-
- closure.best_match = 0;
- closure.prop = property;
- closure.before = extent;
-
- map_extents_bytind (position, position+1, extent_at_mapper,
- (void *) &closure, make_buffer (buf), 0, ME_START_OPEN);
-
- if (!closure.best_match)
- return Qnil;
-
- XSETEXTENT (extent_obj, closure.best_match);
- return extent_obj;
- }
-
- /* ------------------------------- */
- /* verify_extent_modification() */
- /* ------------------------------- */
-
- /* verify_extent_modification() is called when a buffer is modified to
- check whether the modification is occuring inside a read-only extent.
- */
-
- #ifdef ENERGIZE
- extern int inside_parse_buffer; /* total kludge */
- #endif
-
- struct verify_extents_arg
- {
- struct buffer *buf;
- Memind start;
- Memind end;
- };
-
- static int
- verify_extent_mapper (EXTENT extent, void *arg)
- {
- if (extent_read_only_p (extent))
- {
- struct verify_extents_arg *closure = (struct verify_extents_arg *) arg;
-
- /* Allow deletion if the extent is completely contained in
- the region being deleted.
- This is important for supporting tokens which are internally
- write-protected, but which can be killed and yanked as a whole.
- Ignore open/closed distinctions at this point.
- -- Rose
- */
- if (closure->start != closure->end &&
- extent_start (extent) >= closure->start &&
- extent_end (extent) <= closure->end)
- return 0;
-
- {
- Lisp_Object b;
- XSETBUFFER (b, closure->buf);
- while (1)
- Fsignal (Qbuffer_read_only, (list1 (b)));
- }
- }
-
- return 0;
- }
-
- void
- verify_extent_modification (struct buffer *buf, Bytind from, Bytind to)
- {
- int closed;
- struct verify_extents_arg closure;
-
- if (inside_undo
- #ifdef ENERGIZE
- || inside_parse_buffer
- #endif
- )
- return;
-
- /* If insertion, visit closed-endpoint extents touching the insertion
- point because the text would go inside those extents. If deletion,
- treat the range as open on both ends so that touching extents are not
- visited. Note that we assume that an insertion is occurring if the
- changed range has zero length, and a deletion otherwise. This
- fails if a change (i.e. non-insertion, non-deletion) is happening.
- As far as I know, this doesn't currently occur in XEmacs. --ben */
- closed = (from==to);
- closure.buf = buf;
- closure.start = bytind_to_memind (buf, from);
- closure.end = bytind_to_memind (buf, to);
-
- map_extents_bytind (from, to, verify_extent_mapper, (void *) &closure,
- make_buffer (buf), 0,
- closed ? ME_END_CLOSED : ME_START_OPEN);
- }
-
- /* ------------------------------------ */
- /* process_extents_for_insertion() */
- /* ------------------------------------ */
-
- struct process_extents_for_insertion_arg
- {
- Bytind opoint;
- int length;
- struct buffer *buf;
- };
-
- /* A region of length LENGTH was just inserted at OPOINT. Modify all
- of the extents as required for the insertion, based on their
- start-open/end-open properties.
- */
-
- static int
- process_extents_for_insertion_mapper (EXTENT extent, void *arg)
- {
- struct process_extents_for_insertion_arg *closure =
- (struct process_extents_for_insertion_arg *) arg;
- struct buffer *buf = closure->buf;
- Memind index = bytind_to_memind (buf, closure->opoint);
-
- /* When this function is called, one end of the newly-inserted text should
- be adjacent to some endpoint of the extent, or disjoint from it. If
- the insertion overlaps any existing extent, something is wrong.
- */
- #ifdef ERROR_CHECK_EXTENTS
- if (extent_start (extent) > index &&
- extent_start (extent) < index + closure->length)
- abort ();
- if (extent_end (extent) > index &&
- extent_end (extent) < index + closure->length)
- abort ();
- #endif
-
- /* The extent-adjustment code adjusted the extent's endpoints as if
- they were markers -- endpoints at the gap (i.e. the insertion
- point) go to the left of the insertion point, which is correct
- for [) extents. We need to fix the other kinds of extents.
-
- Note that both conditions below will hold for zero-length (]
- extents at the gap. Zero-length () extents would get adjusted
- such that their start is greater than their end; we treat them
- as [) extents. This is unfortunately an inelegant part of the
- extent model, but there is no way around it. */
-
- {
- Memind new_start, new_end;
-
- new_start = extent_start (extent);
- new_end = extent_end (extent);
- if (index == extent_start (extent) && extent_start_open_p (extent) &&
- /* coerce zero-length () extents to [) */
- new_start != new_end)
- new_start += closure->length;
- if (index == extent_end (extent) && !extent_end_open_p (extent))
- new_end += closure->length;
- set_extent_endpoints_1 (extent, new_start, new_end);
- }
-
- return 0;
- }
-
- void
- process_extents_for_insertion (struct buffer *buf, Bytind opoint, int length)
- {
- struct process_extents_for_insertion_arg closure;
-
- closure.opoint = opoint;
- closure.length = length;
- closure.buf = buf;
-
- map_extents_bytind (opoint, opoint + length,
- process_extents_for_insertion_mapper,
- (void *) &closure, make_buffer (buf), 0,
- ME_END_CLOSED | ME_MIGHT_MODIFY_EXTENTS |
- ME_INCLUDE_INTERNAL);
- }
-
- /* ------------------------------------ */
- /* process_extents_for_deletion() */
- /* ------------------------------------ */
-
- struct process_extents_for_deletion_arg
- {
- Memind start, end;
- int destroy_included_extents;
- };
-
- /* This function is called when we're about to delete the range [from, to].
- Detach all of the extents that are completely inside the range [from, to],
- if they're detachable or open-open. */
-
- static int
- process_extents_for_deletion_mapper (EXTENT extent, void *arg)
- {
- struct process_extents_for_deletion_arg *closure =
- (struct process_extents_for_deletion_arg *) arg;
-
- /* If the extent lies completely within the range that
- is being deleted, then nuke the extent if it's detachable
- (otherwise, it will become a zero-length extent). */
-
- if (closure->start <= extent_start (extent) &&
- extent_end (extent) <= closure->end)
- {
- if (extent_detachable_p (extent))
- {
- if (closure->destroy_included_extents)
- destroy_extent (extent);
- else
- extent_detach (extent);
- }
- }
-
- return 0;
- }
-
- /* DESTROY_THEM means destroy the extents instead of just deleting them.
- It is unused currently, but perhaps might be used (there used to
- be a function process_extents_for_destruction(), #if 0'd out,
- that did the equivalent). */
- void
- process_extents_for_deletion (struct buffer *buf, Bytind from,
- Bytind to, int destroy_them)
- {
- struct process_extents_for_deletion_arg closure;
-
- closure.start = bytind_to_memind (buf, from);
- closure.end = bytind_to_memind (buf, to);
- closure.destroy_included_extents = destroy_them;
-
- map_extents_bytind (from, to, process_extents_for_deletion_mapper,
- (void *) &closure, make_buffer (buf), 0,
- ME_END_CLOSED | ME_MIGHT_MODIFY_EXTENTS);
- }
-
-
- /************************************************************************/
- /* extent properties */
- /************************************************************************/
-
- /* return the value of PROPERTY in EXTENT's property list. */
- Lisp_Object
- extent_getf (EXTENT extent, Lisp_Object property)
- {
- Lisp_Object tail = extent_plist (extent);
- Lisp_Object value;
-
- assert (SYMBOLP (property));
- return internal_getf (tail, property, &value) ? value : Qnil;
- }
-
- /* set the value of PROPERTY in EXTENT's property list to VALUE. */
- void
- extent_putf (EXTENT extent, Lisp_Object property, Lisp_Object value)
- {
- Lisp_Object *location = extent_plist_addr (extent);
- assert (SYMBOLP (property));
-
- internal_putf (location, property, value);
- }
-
- void
- set_extent_glyph (EXTENT extent, Lisp_Object glyph, int endp,
- unsigned int layout)
- {
- extent = extent_ancestor (extent);
-
- if (!endp)
- {
- set_extent_begin_glyph (extent, glyph);
- extent_begin_glyph_layout (extent) = layout;
- }
- else
- {
- set_extent_end_glyph (extent, glyph);
- extent_end_glyph_layout (extent) = layout;
- }
-
- extent_changed_for_redisplay (extent, 1);
- }
-
- static Lisp_Object
- glyph_layout_to_symbol (unsigned int layout)
- {
- switch (layout)
- {
- case GL_TEXT: return Qtext;
- case GL_OUTSIDE_MARGIN: return Qoutside_margin;
- case GL_INSIDE_MARGIN: return Qinside_margin;
- case GL_WHITESPACE: return Qwhitespace;
- default: abort ();
- }
- return Qnil; /* shut up compiler */
- }
-
- static unsigned int
- symbol_to_glyph_layout (Lisp_Object layout_obj)
- {
- unsigned int layout = 0;
-
- if (NILP (layout_obj))
- layout = GL_TEXT;
- else
- {
- CHECK_SYMBOL (layout_obj, 0);
- if (EQ (Qoutside_margin, layout_obj))
- layout = GL_OUTSIDE_MARGIN;
- else if (EQ (Qinside_margin, layout_obj))
- layout = GL_INSIDE_MARGIN;
- else if (EQ (Qwhitespace, layout_obj))
- layout = GL_WHITESPACE;
- else if (EQ (Qtext, layout_obj))
- layout = GL_TEXT;
- else
- signal_simple_error ("unknown glyph layout type", layout_obj);
- }
- return layout;
- }
-
- static Lisp_Object
- set_extent_glyph_1 (Lisp_Object extent_obj, Lisp_Object glyph, int endp,
- Lisp_Object layout_obj)
- {
- EXTENT extent = decode_extent (extent_obj, DE_MUST_HAVE_BUFFER);
- unsigned int layout = symbol_to_glyph_layout (layout_obj);
-
- /* Make sure we've actually been given a glyph or it's nil (meaning
- we're deleting a glyph from an extent. */
- if (!NILP (glyph))
- CHECK_GLYPH (glyph, 0);
-
- set_extent_glyph (extent, glyph, endp, layout);
- return glyph;
- }
-
- DEFUN ("set-extent-begin-glyph", Fset_extent_begin_glyph,
- Sset_extent_begin_glyph, 2, 3, 0,
- "Display a bitmap, subwindow or string at the beginning of EXTENT.\n\
- BEGIN-GLYPH must be a glyph object. The layout policy defaults to `text'.")
- (extent, begin_glyph, layout)
- Lisp_Object extent, begin_glyph, layout;
- {
- return set_extent_glyph_1 (extent, begin_glyph, 0, layout);
- }
-
- DEFUN ("set-extent-end-glyph", Fset_extent_end_glyph,
- Sset_extent_end_glyph, 2, 3, 0,
- "Display a bitmap, subwindow or string at the end of the EXTENT.\n\
- END-GLYPH must be a glyph object. The layout policy defaults to `text'.")
- (extent, end_glyph, layout)
- Lisp_Object extent, end_glyph, layout;
- {
- return set_extent_glyph_1 (extent, end_glyph, 1, layout);
- }
-
- DEFUN ("extent-begin-glyph", Fextent_begin_glyph, Sextent_begin_glyph, 1, 1, 0,
- "Return the glyph object displayed at the beginning of EXTENT.\n\
- If there is none, nil is returned.")
- (extent_obj)
- Lisp_Object extent_obj;
- {
- return extent_begin_glyph (decode_extent (extent_obj, 0));
- }
-
- DEFUN ("extent-end-glyph", Fextent_end_glyph, Sextent_end_glyph, 1, 1, 0,
- "Return the glyph object displayed at the end of EXTENT.\n\
- If there is none, nil is returned.")
- (extent_obj)
- Lisp_Object extent_obj;
- {
- return extent_end_glyph (decode_extent (extent_obj, 0));
- }
-
- DEFUN ("set-extent-begin-glyph-layout", Fset_extent_begin_glyph_layout,
- Sset_extent_begin_glyph_layout, 2, 2, 0,
- "Set the layout policy of the given extent's begin glyph.\n\
- Access this using the `extent-begin-glyph-layout' function.")
- (extent, layout)
- Lisp_Object extent, layout;
- {
- EXTENT e = decode_extent (extent, 0);
- e = extent_ancestor (e);
- extent_begin_glyph_layout (e) = symbol_to_glyph_layout (layout);
- extent_maybe_changed_for_redisplay (e, 1);
- return layout;
- }
-
- DEFUN ("set-extent-end-glyph-layout", Fset_extent_end_glyph_layout,
- Sset_extent_end_glyph_layout, 2, 2, 0,
- "Set the layout policy of the given extent's end glyph.\n\
- Access this using the `extent-end-glyph-layout' function.")
- (extent, layout)
- Lisp_Object extent, layout;
- {
- EXTENT e = decode_extent (extent, 0);
- e = extent_ancestor (e);
- extent_end_glyph_layout (e) = symbol_to_glyph_layout (layout);
- extent_maybe_changed_for_redisplay (e, 1);
- return layout;
- }
-
- DEFUN ("extent-begin-glyph-layout", Fextent_begin_glyph_layout,
- Sextent_begin_glyph_layout, 1, 1, 0,
- "Return the layout policy associated with the given extent's begin glyph.\n\
- Set this using the `set-extent-begin-glyph-layout' function.")
- (extent)
- Lisp_Object extent;
- {
- EXTENT e = decode_extent (extent, 0);
- return glyph_layout_to_symbol (extent_begin_glyph_layout (e));
- }
-
- DEFUN ("extent-end-glyph-layout", Fextent_end_glyph_layout,
- Sextent_end_glyph_layout, 1, 1, 0,
- "Return the layout policy associated with the given extent's end glyph.\n\
- Set this using the `set-extent-end-glyph-layout' function.")
- (extent)
- Lisp_Object extent;
- {
- EXTENT e = decode_extent (extent, 0);
- return glyph_layout_to_symbol (extent_end_glyph_layout (e));
- }
-
- DEFUN ("set-extent-priority", Fset_extent_priority, Sset_extent_priority,
- 2, 2, 0,
- "Changes the display priority of EXTENT.\n\
- When the extent attributes are being merged for display, the priority\n\
- is used to determine which extent takes precedence in the event of a\n\
- conflict (two extents whose faces both specify font, for example: the\n\
- font of the extent with the higher priority will be used).\n\
- Extents are created with priority 0; priorities may be negative.")
- (extent, pri)
- Lisp_Object extent, pri;
- {
- EXTENT e = decode_extent (extent, 0);
-
- CHECK_INT (pri, 0);
- e = extent_ancestor (e);
- set_extent_priority (e, XINT (pri));
- extent_maybe_changed_for_redisplay (e, 1);
- return pri;
- }
-
- DEFUN ("extent-priority", Fextent_priority, Sextent_priority, 1, 1, 0,
- "Return the display priority of EXTENT; see `set-extent-priority'.")
- (extent)
- Lisp_Object extent;
- {
- EXTENT e = decode_extent (extent, 0);
- return make_number (extent_priority (e));
- }
-
- DEFUN ("set-extent-property", Fset_extent_property, Sset_extent_property,
- 3, 3, 0,
- "Change a property of an extent.\n\
- PROPERTY may be any symbol; the value stored may be accessed with\n\
- the `extent-property' function.\n\
- The following symbols have predefined meanings:\n\
- \n\
- detached Removes the extent from its buffer; setting this is the same\n\
- as calling `detach-extent'.\n\
- \n\
- destroyed Removes the extent from its buffer, and makes it unusable in\n\
- the future; this is the same calling `delete-extent'.\n\
- \n\
- priority Change redisplay priority; same as `set-extent-priority'.\n\
- \n\
- start-open Whether the set of characters within the extent is treated\n\
- being open on the left, that is, whether the start position\n\
- is an exclusive, rather than inclusive, boundary. If true,\n\
- then characters inserted exactly at the beginning of the\n\
- extent will remain outside of the extent; otherwise they\n\
- will go into the extent, extending it.\n\
- \n\
- end-open Whether the set of characters within the extent is treated\n\
- being open on the right, that is, whether the end position\n\
- is an exclusive, rather than inclusive, boundary. If true,\n\
- then characters inserted exactly at the end of the extent\n\
- will remain outside of the extent; otherwise they will go\n\
- into the extent, extending it.\n\
- \n\
- By default, extents have the `end-open' but not the\n\
- `start-open' property set.\n\
- \n\
- read-only Text within this extent will be unmodifiable.\n\
- \n\
- detachable Whether the extent gets detached (as with `detach-extent')\n\
- when all the text within the extent is deleted. This\n\
- is true by default. If this property is not set, the\n\
- extent becomes a zero-length extent when its text is\n\
- deleted. (In such a case, the `start-open' property is\n\
- automatically removed if both the `start-open' and\n\
- `end-open' properties are set, since zero-length extents\n\
- open on both ends are not allowed.)\n\
- \n\
- face The face in which to display the text. Setting this is the\n\
- same as calling `set-extent-face'.\n\
- \n\
- highlight Highlight the extent when the mouse moves over it.\n\
- \n\
- duplicable Whether this extent should be copied into strings, so that\n\
- kill, yank, and undo commands will restore or copy it.\n\
- \n\
- unique Meaningful only in conjunction with `duplicable'. When this\n\
- is set, there may be only one instance of this extent\n\
- attached at a time: if it is copied to the kill ring and\n\
- then yanked, the extent is not copied. If, however, it is\n\
- killed (removed from the buffer) and then yanked, it will\n\
- be re-attached at the new position.\n\
- \n\
- invisible Text under this extent is treated as not present for the\n\
- purpose of redisplay. The text is still visible to other\n\
- functions that examine a buffer's text, however.\n\
- \n\
- intangible (not yet implemented) Text under this extent is treated as\n\
- not present. Neither redisplay nor any other functions that\n\
- examine a buffer's text will see the text under this extent.\n\
- \n\
- keymap This keymap is consulted for mouse clicks on this extent, or\n\
- keypresses made while point is within the extent.\n\
- \n\
- copy-function This is a hook that is run when a duplicable extent is about\n\
- to be copied from a buffer to a string (or the kill ring).\n\
- It is called with three arguments, the extent, and the\n\
- buffer-positions within it which are being copied. If this\n\
- function returns nil, then the extent will not be copied;\n\
- otherwise it will.\n\
- \n\
- paste-function This is a hook that is run when a duplicable extent is\n\
- about to be copied from a string (or the kill ring) into a\n\
- buffer. It is called with three arguments, the original\n\
- extent, and the buffer positions which the copied extent\n\
- will occupy. (This hook is run after the corresponding text\n\
- has already been inserted into the buffer.) Note that the\n\
- extent argument may be detached when this function is run.\n\
- If this function returns nil, no extent will be inserted.\n\
- Otherwise, there will be an extent covering the range in\n\
- question.\n\
- \n\
- If the original extent is not attached to a buffer, then it\n\
- will be re-attached at this range. Otherwise, a copy will\n\
- be made, and that copy attached here.\n\
- \n\
- The copy-function and paste-function are meaningful only for\n\
- extents with the `duplicable' flag set, and if they are not\n\
- specified, behave as if `t' was the returned value. When\n\
- these hooks are invoked, the current buffer is the buffer\n\
- which the extent is being copied from/to, respectively.")
- (extent, property, value)
- Lisp_Object extent, property, value;
- {
- /* This function can GC if property is `keymap' */
- EXTENT e = decode_extent (extent, 0);
- CHECK_SYMBOL (property, 0);
-
- if (EQ (property, Qread_only))
- extent_read_only_p (e) = !NILP (value);
- else if (EQ (property, Qhighlight))
- extent_highlight_p (e) = !NILP (value);
- else if (EQ (property, Qunique))
- extent_unique_p (e) = !NILP (value);
- else if (EQ (property, Qduplicable))
- extent_duplicable_p (e) = !NILP (value);
- else if (EQ (property, Qinvisible))
- {
- set_extent_invisible (e, !NILP (value));
- }
- else if (EQ (property, Qintangible))
- {
- set_extent_intangible (e, !NILP (value));
- }
- else if (EQ (property, Qdetachable))
- extent_detachable_p (e) = !NILP (value);
-
- else if (EQ (property, Qdetached))
- {
- if (NILP (value)) error ("can only set `detached' to t");
- Fdetach_extent (extent);
- }
- else if (EQ (property, Qdestroyed))
- {
- if (NILP (value)) error ("can only set `destroyed' to t");
- Fdelete_extent (extent);
- }
- else if (EQ (property, Qpriority))
- {
- Fset_extent_priority (extent, value);
- }
- else if (EQ (property, Qface))
- {
- Fset_extent_face (extent, value);
- }
- else if (EQ (property, Qbegin_glyph_layout))
- {
- Fset_extent_begin_glyph_layout (extent, value);
- }
- else if (EQ (property, Qend_glyph_layout))
- {
- Fset_extent_end_glyph_layout (extent, value);
- }
- /* For backwards compatibility. We use begin glyph because it is by
- far the more used of the two. */
- else if (EQ (property, Qglyph_layout))
- {
- Fset_extent_begin_glyph_layout (extent, value);
- }
-
- else if (EQ (property, Qbegin_glyph))
- Fset_extent_begin_glyph (extent, value, Qnil);
-
- else if (EQ (property, Qend_glyph))
- Fset_extent_end_glyph (extent, value, Qnil);
-
- else if (EQ (property, Qstart_open) ||
- EQ (property, Qend_open) ||
- EQ (property, Qstart_closed) ||
- EQ (property, Qend_closed))
- {
- int start_open = -1, end_open = -1;
- if (EQ (property, Qstart_open))
- start_open = !NILP (value);
- else if (EQ (property, Qend_open))
- end_open = !NILP (value);
- /* Support (but don't document...) the obvious antonyms. */
- else if (EQ (property, Qstart_closed))
- start_open = NILP (value);
- else
- end_open = NILP (value);
- set_extent_openness (e, start_open, end_open);
- }
- else
- {
- #ifdef ENERGIZE
- if (EQ (property, Qenergize))
- error ("Thou shalt not change the `energize' extent property");
- #endif
-
- if (EQ (property, Qkeymap))
- while (NILP (Fkeymapp (value)))
- value = wrong_type_argument (Qkeymapp, value);
-
- extent_putf (e, property, value);
- }
-
- return value;
- }
-
- DEFUN ("extent-property", Fextent_property, Sextent_property, 2, 2, 0,
- "Return EXTENT's value for property PROPERTY.\n\
- See `set-extent-property' for the built-in property names.")
- (extent, property)
- Lisp_Object extent, property;
- {
- EXTENT e = decode_extent (extent, 0);
- CHECK_SYMBOL (property, 0);
-
- if (EQ (property, Qdetached))
- return (extent_detached_p (e) ? Qt : Qnil);
- else if (EQ (property, Qdestroyed))
- return (!EXTENT_LIVE_P (e) ? Qt : Qnil);
- #define RETURN_FLAG(flag) \
- return (extent_normal_field (e, flag) ? Qt : Qnil)
- else if (EQ (property, Qstart_open)) RETURN_FLAG (start_open);
- else if (EQ (property, Qend_open)) RETURN_FLAG (end_open);
- else if (EQ (property, Qread_only)) RETURN_FLAG (read_only);
- else if (EQ (property, Qhighlight)) RETURN_FLAG (highlight);
- else if (EQ (property, Qunique)) RETURN_FLAG (unique);
- else if (EQ (property, Qduplicable)) RETURN_FLAG (duplicable);
- else if (EQ (property, Qinvisible)) RETURN_FLAG (invisible);
- else if (EQ (property, Qintangible)) RETURN_FLAG (intangible);
- else if (EQ (property, Qdetachable)) RETURN_FLAG (detachable);
- #undef RETURN_FLAG
- /* Support (but don't document...) the obvious antonyms. */
- else if (EQ (property, Qstart_closed))
- return (extent_start_open_p (e) ? Qnil : Qt);
- else if (EQ (property, Qend_closed))
- return (extent_end_open_p (e) ? Qnil : Qt);
- else if (EQ (property, Qpriority))
- return make_number (extent_priority (e));
- else if (EQ (property, Qface))
- return Fextent_face (extent);
- else if (EQ (property, Qbegin_glyph_layout))
- return Fextent_begin_glyph_layout (extent);
- else if (EQ (property, Qend_glyph_layout))
- return Fextent_end_glyph_layout (extent);
- /* For backwards compatibility. We use begin glyph because it is by
- far the more used of the two. */
- else if (EQ (property, Qglyph_layout))
- return Fextent_begin_glyph_layout (extent);
- else if (EQ (property, Qbegin_glyph))
- return extent_begin_glyph (e);
- else if (EQ (property, Qend_glyph))
- return extent_end_glyph (e);
- else
- return extent_getf (e, property);
- }
-
- DEFUN ("extent-properties", Fextent_properties, Sextent_properties, 1, 1, 0,
- "Return a property list of the attributes of the given extent.\n\
- Do not modify this list; use `set-extent-property' instead.")
- (extent)
- Lisp_Object extent;
- {
- EXTENT e, anc;
- Lisp_Object result, face, anc_obj = Qnil;
-
- CHECK_EXTENT (extent, 0);
- e = XEXTENT (extent);
- if (!EXTENT_LIVE_P (e))
- return Fcons (Qdestroyed, Fcons (Qt, Qnil));
-
- anc = extent_ancestor (e);
- XSETEXTENT (anc_obj, anc);
-
- /* For efficiency, use the ancestor for all properties except detached */
-
- result = extent_plist (anc);
- face = Fextent_face (anc_obj);
- if (!NILP (face))
- result = Fcons (Qface, Fcons (face, result));
-
- /* For now continue to include this for backwards compatibility. */
- if (extent_begin_glyph_layout (anc) != GL_TEXT)
- result = Fcons (Qglyph_layout,
- glyph_layout_to_symbol (extent_begin_glyph_layout (anc)));
-
- if (extent_begin_glyph_layout (anc) != GL_TEXT)
- result = Fcons (Qbegin_glyph_layout,
- glyph_layout_to_symbol (extent_begin_glyph_layout (anc)));
- if (extent_end_glyph_layout (anc) != GL_TEXT)
- result = Fcons (Qend_glyph_layout,
- glyph_layout_to_symbol (extent_end_glyph_layout (anc)));
-
- if (!NILP (extent_end_glyph (anc)))
- result = Fcons (Qend_glyph, Fcons (extent_end_glyph (anc), result));
- if (!NILP (extent_begin_glyph (anc)))
- result = Fcons (Qbegin_glyph, Fcons (extent_begin_glyph (anc), result));
-
- if (extent_priority (anc) != 0)
- result = Fcons (Qpriority, Fcons (make_number (extent_priority (anc)),
- result));
-
- #define CONS_FLAG(flag, sym) if (extent_normal_field (anc, flag)) \
- result = Fcons (sym, Fcons (Qt, result))
- CONS_FLAG (end_open, Qend_open);
- CONS_FLAG (start_open, Qstart_open);
- CONS_FLAG (invisible, Qinvisible);
- CONS_FLAG (intangible, Qintangible);
- CONS_FLAG (detachable, Qdetachable);
- CONS_FLAG (duplicable, Qduplicable);
- CONS_FLAG (unique, Qunique);
- CONS_FLAG (highlight, Qhighlight);
- CONS_FLAG (read_only, Qread_only);
- #undef CONS_FLAG
-
- /* detached is not an inherited property */
- if (extent_detached_p (e))
- result = Fcons (Qdetached, Fcons (Qt, result));
-
- return result;
- }
-
-
- /************************************************************************/
- /* highlighting */
- /************************************************************************/
-
- /* The display code looks into the Vlast_highlighted_extent variable to
- correctly display highlighted extents. This updates that variable,
- and marks the appropriate buffers as needing some redisplay.
- */
- static void
- do_highlight (Lisp_Object extent_obj, int highlight_p)
- {
- if (( highlight_p && (EQ (Vlast_highlighted_extent, extent_obj))) ||
- (!highlight_p && (EQ (Vlast_highlighted_extent, Qnil))))
- return;
- if (EXTENTP (Vlast_highlighted_extent) &&
- EXTENT_LIVE_P (XEXTENT (Vlast_highlighted_extent)))
- {
- /* do not recurse on descendants. Only one extent is highlighted
- at a time. */
- extent_changed_for_redisplay (XEXTENT (Vlast_highlighted_extent), 0);
- }
- Vlast_highlighted_extent = Qnil;
- if (!NILP (extent_obj)
- && BUFFERP (extent_object (XEXTENT (extent_obj)))
- && highlight_p)
- {
- extent_changed_for_redisplay (XEXTENT (extent_obj), 0);
- Vlast_highlighted_extent = extent_obj;
- }
- }
-
- DEFUN ("force-highlight-extent", Fforce_highlight_extent,
- Sforce_highlight_extent, 1, 2, 0,
- "Highlight or unhighlight the given extent.\n\
- If the second arg is non-nil, it will be highlighted, else dehighlighted.\n\
- This is the same as `highlight-extent', except that it will work even\n\
- on extents without the 'highlight property.")
- (extent_obj, highlight_p)
- Lisp_Object extent_obj, highlight_p;
- {
- if (NILP (extent_obj))
- highlight_p = Qnil;
- else
- XSETEXTENT (extent_obj, decode_extent (extent_obj, DE_MUST_BE_ATTACHED));
- do_highlight (extent_obj, !NILP (highlight_p));
- return Qnil;
- }
-
- DEFUN ("highlight-extent", Fhighlight_extent, Shighlight_extent, 1, 2, 0,
- "Highlight the given extent, if it is highlightable\n(\
- that is, if it has the 'highlight property).\n\
- If the second arg is non-nil, it will be highlighted, else dehighlighted.\n\
- Highlighted extents are displayed as if they were merged with the 'highlight\n\
- face.")
- (extent_obj, highlight_p)
- Lisp_Object extent_obj, highlight_p;
- {
- if (EXTENTP (extent_obj) && !extent_highlight_p (XEXTENT (extent_obj)))
- return Qnil;
- else
- return (Fforce_highlight_extent (extent_obj, highlight_p));
- }
-
-
- /************************************************************************/
- /* extent replicas */
- /************************************************************************/
-
- /* #### All of this shit needs to be reviewed. I personally think that
- extent replicas should be trashed and extents should just be extended
- so they work over strings as well as buffers. --ben */
-
- /* copy/paste hooks */
-
- static int
- run_extent_copy_paste_internal (EXTENT e, Bufpos from, Bufpos to,
- Lisp_Object buffer,
- Lisp_Object prop)
- {
- /* This function can GC */
- Lisp_Object extent;
- Lisp_Object copy_fn;
- XSETEXTENT (extent, e);
- copy_fn = Fextent_property (extent, prop);
- if (!NILP (copy_fn))
- {
- Lisp_Object flag;
- struct gcpro gcpro1, gcpro2, gcpro3;
- GCPRO3 (extent, copy_fn, buffer);
- flag = call3_in_buffer (XBUFFER (buffer), copy_fn, extent,
- make_number (from), make_number (to));
- UNGCPRO;
- if (NILP (flag) || !EXTENT_LIVE_P (XEXTENT (extent)))
- return 0;
- }
- return 1;
- }
-
- static int
- run_extent_copy_function_bufpos (EXTENT e, Bufpos from, Bufpos to)
- {
- /* This function can GC */
- return run_extent_copy_paste_internal (e, from, to, extent_object (e),
- Qcopy_function);
- }
-
- static int
- run_extent_paste_function (EXTENT e, Bytind from, Bytind to,
- struct buffer *buf)
- {
- /* This function can GC */
- return run_extent_copy_paste_internal (e, bytind_to_bufpos (buf, from),
- bytind_to_bufpos (buf, to),
- make_buffer (buf),
- Qpaste_function);
- }
-
- static void
- update_extent (EXTENT extent, Bytind from, Bytind to)
- {
- set_extent_endpoints (extent, from, to);
- #ifdef ENERGIZE
- restore_energize_extent_state (extent);
- #endif
- }
-
- /* Insert an extent, usually from the dup_list of a string which
- has just been inserted.
- This code does not handle the case of undo.
- */
- static Lisp_Object
- insert_extent (EXTENT extent, Bytind new_start, Bytind new_end,
- struct buffer *buf, int run_hooks)
- {
- /* This function can GC */
- Lisp_Object tmp;
-
- if (!BUFFERP (extent_object (extent)))
- goto copy_it;
- if (XBUFFER (extent_object (extent)) != buf)
- goto copy_it;
-
- if (extent_detached_p (extent))
- {
- if (run_hooks &&
- !run_extent_paste_function (extent, new_start, new_end, buf))
- /* The paste-function said don't re-attach this extent here. */
- return Qnil;
- else
- update_extent (extent, new_start, new_end);
- }
- else
- {
- Bytind exstart = extent_endpoint_bytind (extent, 0);
- Bytind exend = extent_endpoint_bytind (extent, 1);
-
- if (exend < new_start || exstart > new_end)
- goto copy_it;
- else
- {
- new_start = min (exstart, new_start);
- new_end = max (exend, new_end);
- if (exstart != new_start || exend != new_end)
- update_extent (extent, new_start, new_end);
- }
- }
-
- XSETEXTENT (tmp, extent);
- return tmp;
-
- copy_it:
- if (run_hooks &&
- !run_extent_paste_function (extent, new_start, new_end, buf))
- /* The paste-function said don't attach a copy of the extent here. */
- return Qnil;
- else
- {
- XSETEXTENT (tmp, copy_extent (extent, new_start, new_end,
- make_buffer (buf)));
- return tmp;
- }
- }
-
- DEFUN ("insert-extent", Finsert_extent, Sinsert_extent, 1, 4, 0,
- "Insert EXTENT from START to END in the current buffer.\n\
- This operation does not insert any characters,\n\
- but otherwise acts like `insert' of a string whose\n\
- string-extent-data calls for EXTENT to be inserted.\n\
- Returns the newly-inserted extent.\n\
- The fourth arg, NO-HOOKS, can be used to inhibit the running of the\n\
- extent's `paste-function' property if it has one.\n\
- See documentation on `detach-extent' for a discussion of undo recording.")
- (extent, start, end, no_hooks)
- Lisp_Object extent, start, end, no_hooks;
- {
- EXTENT ext = decode_extent (extent, 0);
- Lisp_Object copy;
- Bytind s, e;
-
- get_bufrange_bytind (current_buffer, start, end, &s, &e,
- GB_ALLOW_PAST_ACCESSIBLE);
-
- copy = insert_extent (ext, s, e, current_buffer, NILP (no_hooks));
- if (EXTENTP (copy))
- {
- if (extent_duplicable_p (XEXTENT (copy)))
- record_extent (copy, 1);
- }
- return copy;
- }
-
- /* #### A lot of this stuff is going to change, don't use it yet -- jwz */
-
- DEFUN ("string-extent-data", Fstring_extent_data, Sstring_extent_data, 1, 1, 0,
- "Return the saved extent data associated with the given string.\n\
- \n\
- NOTE: this function may go away in the future, in favor of making\n\
- `map-extents' accept a string as an argument.\n\
- \n\
- The format is a list of extent-replica objects, each with an extent\n\
- and start and end positions within the string itself.\n\
- Set this using the `set-string-extent-data' function.\n\
- \n\
- The `concat' function logically concatenates this list, reconstructing\n\
- the extent information with adjusted start and end positions.\n\
- \n\
- When `buffer-substring' or a similar function creates a string,\n\
- it stores an entry on this list for every `duplicable' extent overlapping\n\
- the string. See `set-extent-property'.\n\
- \n\
- When `insert' or a similar function inserts the string into a buffer,\n\
- each saved extent is copied into the buffer. If the saved extent is\n\
- already in the buffer at an adjacent location, it is extended. If the\n\
- saved extent is detached from the buffer, it is reattached. If the saved\n\
- extent is already attached, or is detached from a different buffer, it is\n\
- copied as if by `copy-extent', and the extent's `paste-function' is\n\
- consulted. This entire sequence of events is also available in the\n\
- function `insert-extent'.")
- (string)
- Lisp_Object string;
- {
- CHECK_STRING (string, 0);
- return string_dups (XSTRING (string));
- }
-
- DEFUN ("set-string-extent-data", Fset_string_extent_data,
- Sset_string_extent_data, 2, 2, 0,
- "Set the saved extent data associated with the given string.\n\
- Access this using the `string-extent-data' function.")
- (string, data)
- Lisp_Object string, data;
- {
- CHECK_STRING (string, 0);
- CHECK_LIST (data, 1);
- CHECK_IMPURE (string);
-
- set_string_dups (XSTRING (string), data);
- return string;
- }
-
- static EXTENT_REPLICA
- decode_extent_replica (Lisp_Object obj)
- {
- CHECK_LIVE_EXTENT_REPLICA (obj, 0);
- return XEXTENT_REPLICA (obj);
- }
-
- /* Extent replica goo.
- This is a read-only data structure.
- As far as the Lisp programmer is concerned, it is used ONLY as a carrier for
- string-extent-data information.
- */
- DEFUN ("make-extent-replica", Fmake_extent_replica, Smake_extent_replica,
- 3, 3, 0,
- "Make an object suitable for use with `set-string-extent-data'.\n\
- The arguments are EXTENT, START, and END.\n\
- There are no mutator functions for this data structure, only accessors.")
- (extent, start, end)
- Lisp_Object extent, start, end;
- {
- EXTENT_REPLICA dup;
- Lisp_Object res;
-
- CHECK_LIVE_EXTENT (extent, 0);
- CHECK_INT_COERCE_MARKER (start, 1);
- CHECK_INT_COERCE_MARKER (end, 2);
-
- dup = make_extent_replica (extent, XINT (start), XINT (end));
- XSETEXTENT_REPLICA (res, dup);
- return res;
- }
-
- DEFUN ("extent-replica-p", Fextent_replica_p, Sextent_replica_p, 1, 1, 0,
- "T if OBJECT is an extent replica.")
- (object)
- Lisp_Object object;
- {
- if (EXTENT_REPLICAP (object))
- return Qt;
- return Qnil;
- }
-
- DEFUN ("extent-replica-live-p", Fextent_replica_live_p, Sextent_replica_live_p,
- 1, 1, 0,
- "T if OBJECT is an extent replica that has not been destroyed.")
- (object)
- Lisp_Object object;
- {
- if (EXTENT_REPLICA_LIVE_P (XEXTENT_REPLICA (object)))
- return Qt;
- return Qnil;
- }
-
- DEFUN ("extent-replica-extent", Fextent_replica_extent, Sextent_replica_extent,
- 1, 1, 0,
- "Return the extent of the specified extent replica.\n\
- See `make-extent-replica'.")
- (extent_replica)
- Lisp_Object extent_replica;
- {
- return extent_replica_extent (decode_extent_replica (extent_replica));
- }
-
- DEFUN ("extent-replica-start", Fextent_replica_start, Sextent_replica_start,
- 1, 1, 0,
- "Return the start of the specified extent replica.\n\
- See `make-extent-replica'.")
- (extent_replica)
- Lisp_Object extent_replica;
- {
- return make_number (extent_replica_start
- (decode_extent_replica (extent_replica)));
- }
-
- DEFUN ("extent-replica-end", Fextent_replica_end, Sextent_replica_end,
- 1, 1, 0,
- "Return the end of the specified extent replica.\n\
- See `make-extent-replica'.")
- (extent_replica)
- Lisp_Object extent_replica;
- {
- return make_number (extent_replica_end
- (decode_extent_replica (extent_replica)));
- }
-
-
-
- /* replicating extents */
-
- struct replicate_extents_arg
- {
- Bufpos from;
- Charcount length;
- struct buffer *buf;
- Lisp_Object head;
- Lisp_Object nconc_cell;
- };
-
- static int
- replicate_extents_mapper (EXTENT extent, void *arg)
- {
- /* This function can GC */
- struct replicate_extents_arg *closure =
- (struct replicate_extents_arg *) arg;
- Lisp_Object head = closure->head;
- Lisp_Object tail = closure->nconc_cell;
- Charcount start = extent_endpoint_bufpos (extent, 0) - closure->from;
- Charcount end = extent_endpoint_bufpos (extent, 1) - closure->from;
-
- if (inside_undo || extent_duplicable_p (extent))
- {
- start = max (start, 0);
- end = min (end, closure->length);
-
- /* Run the copy-function to give an extent the option of
- not being copied into the string (or kill ring).
- */
- if (extent_duplicable_p (extent) &&
- !run_extent_copy_function_bufpos (extent,
- start + closure->from,
- end + closure->from))
- return 0;
-
- /* Make a dup and put it on the string-extent-data. */
- {
- Lisp_Object new_cell;
- Lisp_Object replica;
- EXTENT_REPLICA dup;
-
- XSETEXTENT (replica, extent);
- dup = make_extent_replica (replica, start, end);
- XSETEXTENT_REPLICA (replica, dup);
- new_cell = Fcons (replica, Qnil);
-
- if (NILP (head))
- closure->head = new_cell;
- else
- Fsetcdr (tail, new_cell);
- closure->nconc_cell = new_cell;
- }
- }
- return 0;
- }
-
- Lisp_Object
- replicate_extents (struct buffer *buf, Bufpos opoint, Charcount length)
- {
- /* This function can GC */
- struct replicate_extents_arg closure;
-
- closure.from = opoint;
- closure.length = length;
- closure.head = Qnil;
- closure.buf = buf;
- closure.nconc_cell = Qzero;
- map_extents (opoint, opoint + length, replicate_extents_mapper,
- (void *) &closure, make_buffer (buf), 0,
- /* ignore extents that just abut the region */
- ME_END_CLOSED | ME_ALL_EXTENTS_OPEN |
- /* we are calling E-Lisp (the extent's copy function)
- so anything might happen */
- ME_MIGHT_CALL_ELISP);
- return closure.head;
- }
-
- /* We have just inserted a string of size "length" at "opoint"; the string
- was taken from an original string at position pos. We have the contents
- of the extents slot of the original string on hand, and we now need
- to do "whatever" is necessary to make the extents in the buffer be
- correctly updated. If there are no extents on the string, then that is
- nothing. If there are extents and we are inside_undo, then the extents
- argument is taken as revealed truth and the state of the buffer extents
- must be restored so that the function above would return the same string
- extents if this corresponding string were to be deleted. If we are not
- inside undo then we just splice in those extents that correspond to
- deleted extents.
-
- Note: At the moment we ONLY handle the case of the dup_list argument
- be a list of extent_replicas.
- */
-
- void
- splice_in_extent_replicas (struct buffer *buf, Bufpos opoint,
- Charcount length, Charcount pos,
- Lisp_Object dup_list)
- {
- Lisp_Object tail;
- Bufpos base_start = opoint;
- Bufpos base_end = opoint + length;
-
- if (NILP (dup_list))
- return;
- assert (CONSP (dup_list));
-
- for (tail = dup_list; !NILP (tail); tail = Fcdr (tail))
- {
- Lisp_Object current_replica = Fcar (tail);
- /* only process replicas at the moment */
- if (EXTENT_REPLICAP (current_replica))
- {
- EXTENT_REPLICA dup = XEXTENT_REPLICA (current_replica);
- EXTENT extent = XEXTENT (extent_replica_extent (dup));
- Bufpos new_start = base_start + extent_replica_start (dup) - pos;
- Bufpos new_end = base_start + extent_replica_end (dup) - pos;
- Bufpos exstart = 0;
- Bufpos exend = 0;
-
- if (!EXTENT_LIVE_P (extent))
- continue;
-
- if (!extent_detached_p (extent))
- {
- exstart = extent_endpoint_bufpos (extent, 0);
- exend = extent_endpoint_bufpos (extent, 1);
- }
-
- #if 0
- /* utter hogwash. The "invalid" condition that this code
- was attempting to catch is in fact quite valid, and occurs
- often with text properties, because of the way the text-
- property mechanism re-uses existing extents. I don't
- know why this code was in here in the first place, other
- than a braino on the part of the original implementor. */
- if (inside_undo)
- {
- if (!extent_detached_p (extent) &&
- (exend > base_end || exstart < base_start))
- error ("extent 0x%lx is all fouled up wrt. dup 0x%lx",
- (long) extent, (long) dup);
- }
- #endif
-
- /* The extra comparisons defend against set-string-extent-data
- and support insert_lisp_string. */
- if (new_start < base_start)
- new_start = base_start;
- if (new_end > base_end)
- new_end = base_end;
- if (new_end <= new_start)
- continue;
-
- #ifdef ENERGIZE
- /* Energize extents like toplevel-forms can only be pasted
- in the buffer they come from. This should be parametrized
- in the generic extent objects. Right now just silently
- skip the extents if it's not from the same buffer.
- */
- if (XBUFFER (extent_object (extent)) != buf
- && energize_extent_data (extent))
- continue;
- #endif
-
- /* If this is a `unique' extent, and it is currently attached
- somewhere other than here (non-overlapping), then don't copy
- it (that's what `unique' means). If however it is detached,
- or if we are inserting inside/adjacent to the original
- extent, then insert_extent() will simply reattach it, which
- is what we want.
- */
- if (extent_unique_p (extent)
- && !extent_detached_p (extent)
- && (XBUFFER (extent_object (extent)) != buf
- || exend > new_end
- || exstart < new_start))
- continue;
-
- insert_extent (extent, bufpos_to_bytind (buf, new_start),
- bufpos_to_bytind (buf, new_end), buf, !inside_undo);
- }
- }
- }
-
- static void
- add_to_replicas_lists (c_hashtable table,
- Lisp_Object dup_list,
- Charcount offset, Charcount length,
- int clip_parts, Charcount total_length,
- Lisp_Object *cells_vec)
- {
- Lisp_Object tail;
- for (tail = dup_list; !NILP (tail); tail = Fcdr (tail))
- {
- Lisp_Object current_replica = Fcar (tail);
- if (EXTENT_REPLICAP (current_replica))
- {
- EXTENT_REPLICA dup = XEXTENT_REPLICA (current_replica);
- Bufpos new_start = extent_replica_start (dup);
- Bufpos new_end = extent_replica_end (dup);
- EXTENT extent = XEXTENT (extent_replica_extent (dup));
- Lisp_Object pre_existing_cell;
- Lisp_Object tmp;
- EXTENT_REPLICA new_dup;
- CONST void *vval;
-
- if (clip_parts)
- {
- /* The extra clipping defends against set-string-extent-data.
- It is not necessary in shift_replicas, since the
- check against total_length still applies below.
- */
- if (new_start > length) new_start = length;
- if (new_end > length) new_end = length;
- }
-
- new_start += offset;
- new_end += offset;
-
- /* These checks are needed because of Fsubstring, and are a good
- idea in any case:
- */
- if (new_end <= 0)
- continue;
- if (new_start >= total_length)
- continue;
- if (new_start <= 0)
- new_start = 0;
- if (new_end >= total_length)
- new_end = total_length;
-
- if (!EXTENT_LIVE_P (extent))
- continue;
-
- new_dup = make_extent_replica (extent_replica_extent (dup),
- new_start, new_end);
-
- if (!gethash ((void *) extent, table, &vval))
- pre_existing_cell = Qnil;
- else
- VOID_TO_LISP (pre_existing_cell, vval);
-
- XSETEXTENT_REPLICA (tmp, new_dup);
- tmp = Fcons (tmp, pre_existing_cell);
- puthash (extent, LISP_TO_VOID (tmp), table);
- }
- #if 0
- else
- {
- /* Save away misc. trash in the order encountered. */
- Lisp_Object cell;
- cell = Fcons (current_replica, Qnil);
- if (NILP (cells_vec[0]))
- cells_vec[0] = cell;
- else
- nconc2 (cells_vec[1], cell);
- cells_vec[1] = cell;
- }
- #endif
- }
- }
-
- /* Merge dup_list[i] into a list of replicas -- if a dup
- in listi "overlaps at the end" matches a dup from listi+1 that "overlaps
- at the beginning", merge them into one contiguous dup in the returned
- list. It is weird and probably bogus if a "detached dup" doesn't merge
- entirely, but it isn't an error.
-
- This code also handles construction of a dup_list for Fsubstring,
- by handing in a single list with a possibly negative offset and
- a length which is possibly less than the length of the original string.
- */
-
- static void
- merge_replicas_concating_mapper (CONST void *key, void *contents, void *arg)
- {
- Lisp_Object extent_cell;
- Lisp_Object *cells_vec = (Lisp_Object *) arg;
- VOID_TO_LISP (extent_cell, contents);
-
- if (NILP (cells_vec[0]))
- cells_vec[0] = extent_cell;
- else
- nconc2 (cells_vec[1], extent_cell);
-
- cells_vec[1] = extent_cell;
- return;
- }
-
- static int
- mrp_pred (Lisp_Object x, Lisp_Object y, Lisp_Object dummy)
- {
- EXTENT_REPLICA dup1 = XEXTENT_REPLICA (x);
- EXTENT_REPLICA dup2 = XEXTENT_REPLICA (y);
-
- if (extent_replica_start (dup1) < extent_replica_start (dup2))
- return 1;
- else if (extent_replica_start (dup1) == extent_replica_start (dup2))
- {
- if (extent_replica_end (dup1) <= extent_replica_end (dup2))
- return 1;
- else
- return -1;
- }
- return -1;
- }
-
- static void
- merge_replicas_pruning_mapper (CONST void *key, void *contents, void *arg)
- {
- Lisp_Object dup_list;
- c_hashtable table = (c_hashtable) arg;
- VOID_TO_LISP (dup_list, contents);
-
- if (NILP (dup_list))
- return;
- if (NILP (Fcdr (dup_list)))
- return;
-
- /* sort and merge the dup_list */
- dup_list = list_sort (dup_list, Qnil, mrp_pred);
- {
- Lisp_Object current = dup_list;
- Lisp_Object tail = Fcdr (dup_list);
- EXTENT_REPLICA current_dup = XEXTENT_REPLICA (Fcar (current));
-
- while (!NILP (tail))
- {
- EXTENT_REPLICA tail_dup = XEXTENT_REPLICA (Fcar (tail));
-
- if (extent_replica_start (tail_dup) <=
- extent_replica_end (current_dup) - 1)
- {
- set_extent_replica_end (current_dup,
- max (extent_replica_end (tail_dup),
- extent_replica_end (current_dup)));
- Fsetcdr (current, Fcdr (tail));
- }
- else
- {
- current = tail;
- current_dup = XEXTENT_REPLICA (Fcar (current));
- }
-
- tail = Fcdr (tail);
- }
- }
-
- /* now put back the munged list */
- puthash (key, LISP_TO_VOID (dup_list), table);
- }
-
- static Lisp_Object
- merge_replicas_internal (int number_of_lists,
- struct merge_replicas_struct *vec,
- int shiftp)
- {
- c_hashtable table = 0;
- Lisp_Object cells_vec[2];
- int i;
- int total_length;
- int clip_parts = !shiftp;
-
- cells_vec[0] = Qnil;
- cells_vec[1] = Qnil;
-
- total_length = 0;
- for (i = 0; i < number_of_lists; i++)
- total_length += vec[i].entry_length;
-
- for (i = 0; i < number_of_lists; i++)
- {
- Lisp_Object dup_list = vec[i].dup_list;
- Charcount offset = vec[i].entry_offset;
- Charcount length = vec[i].entry_length;
-
- if (!NILP (dup_list))
- {
- if (!table)
- table = make_hashtable (10);
- add_to_replicas_lists (table, dup_list,
- offset, length,
- clip_parts, total_length,
- cells_vec);
- }
- }
-
- if (table)
- {
- maphash (merge_replicas_pruning_mapper, table, (void*)table);
- maphash (merge_replicas_concating_mapper, table, (void*)&(cells_vec[0]));
- free_hashtable (table);
- }
- return (cells_vec[0]);
- }
-
- Lisp_Object
- merge_replicas (int number_of_lists, struct merge_replicas_struct *vec)
- {
- return merge_replicas_internal (number_of_lists, vec, 0);
- }
-
- /* Like merge_replicas, but operates on just one dup_list,
- applying an offset and clipping the results to [0..length).
- The offset is non-positive if the caller is Fsubstring.
- */
- Lisp_Object
- shift_replicas (Lisp_Object dup_list, int offset, int length)
- {
- struct merge_replicas_struct mr_struct;
- mr_struct.dup_list = dup_list;
- mr_struct.entry_offset = offset;
- mr_struct.entry_length = length;
- return merge_replicas_internal (1, &mr_struct, 1);
- }
-
-
-
- /* Checklist for sanity checking:
- - {kill, yank, copy} at {open, closed} {start, end} of {writable, read-only} extent
- - {kill, copy} & yank {once, repeatedly} duplicable extent in {same, different} buffer
- */
-
-
- /************************************************************************/
- /* text properties */
- /************************************************************************/
-
- /* Text properties
- Originally this stuff was implemented in lisp (all of the functionality
- exists to make that possible) but speed was a problem.
- */
-
- Lisp_Object Qtext_prop;
- Lisp_Object Qtext_prop_extent_paste_function;
-
- struct put_text_prop_arg
- {
- Lisp_Object prop, value; /* The property and value we are storing */
- Bytind start, end; /* The region into which we are storing it */
- struct buffer *buffer;
- int changed_p; /* Output: whether we have modified anything */
- Lisp_Object the_extent; /* Our chosen extent; this is used for
- communication between subsequent passes. */
- };
-
- static int
- put_text_prop_mapper (EXTENT e, void *arg)
- {
- struct put_text_prop_arg *closure = (struct put_text_prop_arg *) arg;
-
- Lisp_Object value = closure->value;
- Bytind e_start, e_end;
- Bytind start = closure->start;
- Bytind end = closure->end;
- Lisp_Object extent, e_val;
- XSETEXTENT (extent, e);
- e_start = extent_endpoint_bytind (e, 0);
- e_end = extent_endpoint_bytind (e, 1);
- e_val = Fextent_property (extent, closure->prop);
-
- if (!EQ (Fextent_property (extent, Qtext_prop), closure->prop))
- {
- /* It's not for this property; do nothing. */
- ;
- }
- else if (!NILP (value) &&
- NILP (closure->the_extent) &&
- EQ (value, e_val))
- {
- /* We want there to be an extent here at the end, and we haven't picked
- one yet, so use this one. Extend it as necessary. We only reuse an
- extent which has an EQ value for the prop in question to avoid
- side-effecting the kill ring (that is, we never change the property
- on an extent after it has been created.)
- */
- if (e_start != start || e_end != end)
- {
- set_extent_endpoints (e, min (e_start, start),
- max (e_end, end));
- closure->changed_p = 1;
- }
- closure->the_extent = extent;
- }
-
- /* Even if we're adding a prop, at this point, we want all other extents of
- this prop to go away (as now they overlap). So the theory here is that,
- when we are adding a prop to a region that has multiple (disjoint)
- occurences of that prop in it already, we pick one of those and extend
- it, and remove the others.
- */
-
- else if (EQ (extent, closure->the_extent))
- {
- /* just in case map-extents hits it again (does that happen?) */
- ;
- }
- else if (e_start >= start && e_end <= end)
- {
- /* Extent is contained in region; remove it. Don't destroy or modify
- it, because we don't want to change the attributes pointed to by the
- duplicates in the kill ring.
- */
- extent_detach (e);
- closure->changed_p = 1;
- }
- else if (!NILP (closure->the_extent) &&
- EQ (value, e_val) &&
- e_start <= end &&
- e_end >= start)
- {
- EXTENT te = XEXTENT (closure->the_extent);
- /* This extent overlaps, and has the same prop/value as the extent we've
- decided to reuse, so we can remove this existing extent as well (the
- whole thing, even the part outside of the region) and extend
- the-extent to cover it, resulting in the minimum number of extents in
- the buffer.
- */
- Bytind the_start = extent_endpoint_bytind (te, 0);
- Bytind the_end = extent_endpoint_bytind (te, 1);
- if (e_start != the_start && /* note AND not OR */
- e_end != the_end)
- {
- set_extent_endpoints (te,
- min (the_start, e_start),
- max (the_end, e_end));
- closure->changed_p = 1;
- }
- extent_detach (e);
- }
- else if (e_end <= end)
- {
- /* Extent begins before start but ends before end, so we can just
- decrease its end position.
- */
- if (e_end != start)
- {
- set_extent_endpoints (e, e_start, start);
- closure->changed_p = 1;
- }
- }
- else if (e_start >= start)
- {
- /* Extent ends after end but begins after start, so we can just
- increase its start position.
- */
- if (e_start != end)
- {
- set_extent_endpoints (e, end, e_end);
- closure->changed_p = 1;
- }
- }
- else
- {
- /* Otherwise, `extent' straddles the region. We need to split it.
- */
- set_extent_endpoints (e, e_start, start);
- copy_extent (e, end, e_end, extent_object (e));
- closure->changed_p = 1;
- }
-
- return 0; /* to continue mapping. */
- }
-
- static int
- put_text_prop (Bytind start, Bytind end, struct buffer *b,
- Lisp_Object prop, Lisp_Object value,
- int duplicable_p)
- {
- /* This function can GC */
- struct put_text_prop_arg closure;
- if (start == end) /* There are no characters in the region. */
- return 0;
-
- closure.prop = prop;
- closure.value = value;
- closure.start = start;
- closure.end = end;
- closure.buffer = b;
- closure.changed_p = 0;
- closure.the_extent = Qnil;
-
- map_extents_bytind (start, end,
- put_text_prop_mapper,
- (void *) &closure, make_buffer (b), 0,
- /* get all extents that abut the region */
- ME_ALL_EXTENTS_CLOSED | ME_END_CLOSED |
- ME_MIGHT_MODIFY_EXTENTS);
-
- /* If we made it through the loop without reusing an extent
- (and we want there to be one) make it now.
- */
- if (!NILP (value) && NILP (closure.the_extent))
- {
- Lisp_Object extent = Qnil;
- Lisp_Object object = Qnil;
- XSETBUFFER (object, b);
- XSETEXTENT (extent, make_extent_internal (object, start, end));
- closure.changed_p = 1;
- Fset_extent_property (extent, Qtext_prop, prop);
- Fset_extent_property (extent, prop, value);
- if (duplicable_p)
- {
- extent_duplicable_p (XEXTENT (extent)) = 1;
- Fset_extent_property (extent, Qpaste_function,
- Qtext_prop_extent_paste_function);
- }
- }
-
- return closure.changed_p;
- }
-
- DEFUN ("put-text-property", Fput_text_property, Sput_text_property, 4, 5, 0,
- "Adds the given property/value to all characters in the specified region.\n\
- The property is conceptually attached to the characters rather than the\n\
- region. The properties are copied when the characters are copied/pasted.")
- (start, end, prop, value, buffer)
- Lisp_Object start, end, prop, value, buffer;
- {
- /* This function can GC */
- Bytind s, e;
- struct buffer *b = decode_buffer (buffer, 0);
- get_bufrange_bytind (b, start, end, &s, &e, 0);
- CHECK_SYMBOL (prop, 0);
- put_text_prop (s, e, b, prop, value, 1);
- return prop;
- }
-
- DEFUN ("put-nonduplicable-text-property", Fput_nonduplicable_text_property,
- Sput_nonduplicable_text_property, 4, 5, 0,
- "Adds the given property/value to all characters in the specified region.\n\
- The property is conceptually attached to the characters rather than the\n\
- region, however the properties will not be copied when the characters\n\
- are copied.")
- (start, end, prop, value, buffer)
- Lisp_Object start, end, prop, value, buffer;
- {
- /* This function can GC */
- Bytind s, e;
- struct buffer *b = decode_buffer (buffer, 0);
- get_bufrange_bytind (b, start, end, &s, &e, 0);
- CHECK_SYMBOL (prop, 0);
- put_text_prop (s, e, b, prop, value, 0);
- return prop;
- }
-
- DEFUN ("add-text-properties", Fadd_text_properties, Sadd_text_properties,
- 3, 4, 0,
- "Add properties to the characters from START to END.\n\
- The third argument PROPS is a property list specifying the property values\n\
- to add. The optional fourth argument, OBJECT, is the buffer containing the\n\
- text. Returns t if any property was changed, nil otherwise.")
- (start, end, props, buffer)
- Lisp_Object start, end, props, buffer;
- {
- /* This function can GC */
- int changed = 0;
- Bytind s, e;
- struct buffer *b = decode_buffer (buffer, 0);
- get_bufrange_bytind (b, start, end, &s, &e, 0);
- CHECK_LIST (props, 0);
- for (; !NILP (props); props = Fcdr (Fcdr (props)))
- {
- Lisp_Object prop = XCAR (props);
- Lisp_Object value = Fcar (XCDR (props));
- CHECK_SYMBOL (prop, 0);
- changed |= put_text_prop (s, e, b, prop, value, 1);
- }
- return (changed ? Qt : Qnil);
- }
-
- DEFUN ("remove-text-properties", Fremove_text_properties,
- Sremove_text_properties, 3, 4, 0,
- "Remove the given properties from all characters in the specified region.\n\
- PROPS should be a plist, but the values in that plist are ignored (treated\n\
- as nil). Returns t if any property was changed, nil otherwise.")
- (start, end, props, buffer)
- Lisp_Object start, end, props, buffer;
- {
- /* This function can GC */
- int changed = 0;
- Bytind s, e;
- struct buffer *b = decode_buffer (buffer, 0);
- get_bufrange_bytind (b, start, end, &s, &e, 0);
- CHECK_LIST (props, 0);
- for (; !NILP (props); props = Fcdr (Fcdr (props)))
- {
- Lisp_Object prop = XCAR (props);
- CHECK_SYMBOL (prop, 0);
- changed |= put_text_prop (s, e, b, prop, Qnil, 1);
- }
- return (changed ? Qt : Qnil);
- }
-
- /* Whenever a text-prop extent is pasted into a buffer (via `yank' or `insert'
- or whatever) we attach the properties to the buffer by calling
- `put-text-property' instead of by simply allowing the extent to be copied or
- re-attached. Then we return nil, telling the extents code not to attach it
- again. By handing the insertion hackery in this way, we make kill/yank
- behave consistently with put-text-property and not fragment the extents
- (since text-prop extents must partition, not overlap).
-
- The lisp implementation of this was probably fast enough, but since I moved
- the rest of the put-text-prop code here, I moved this as well for
- completeness.
- */
- DEFUN ("text-prop-extent-paste-function", Ftext_prop_extent_paste_function,
- Stext_prop_extent_paste_function, 3, 3, 0,
- "Used as the `paste-function' property of `text-prop' extents.")
- (extent, from, to)
- Lisp_Object extent, from, to;
- {
- /* This function can GC */
- Lisp_Object prop, val;
- prop = Fextent_property (extent, Qtext_prop);
- if (NILP (prop))
- signal_simple_error ("internal error: no text-prop", extent);
- val = Fextent_property (extent, prop);
- if (NILP (val))
- signal_simple_error_2 ("internal error: no text-prop",
- extent, prop);
- Fput_text_property (from, to, prop, val, Qnil);
- return Qnil; /* important! */
- }
-
- /* This function could easily be written in Lisp but the C code wants
- to use it in connection with invisible extents (at least currently).
- If this changes, consider moving this back into Lisp. */
-
- DEFUN ("next-single-property-change", Fnext_single_property_change,
- Snext_single_property_change, 2, 4, 0,
- "Return the position of next property change for a specific property.\n\
- Scans characters forward from POS till it finds a change in the PROP\n\
- property, then returns the position of the change. The optional third\n\
- argument BUFFER is the buffer to scan (defaults to the current buffer).\n\
- The property values are compared with `eq'.\n\
- Return nil if the property is constant all the way to the end of BUFFER.\n\
- If the value is non-nil, it is a position greater than POS, never equal.\n\n\
- If the optional fourth argument LIMIT is non-nil, don't search\n\
- past position LIMIT; return LIMIT if nothing is found before LIMIT.\n\
- If two or more extents with conflicting non-nil values for PROP overlap\n\
- a particular character, it is undefined which value is considered to be\n\
- the value of PROP. (Note that this situation will not happen if you always\n\
- use the text-property primitives.)")
- (pos, prop, buffer, limit)
- Lisp_Object pos, prop, buffer, limit;
- {
- struct buffer *buf = decode_buffer (buffer, 0);
- Bufpos bpos = get_bufpos (buf, pos, 0);
- Bufpos blim;
- Lisp_Object extent, value;
- int limit_was_nil;
-
- if (NILP (limit))
- {
- blim = BUF_ZV (buf);
- limit_was_nil = 1;
- }
- else
- {
- blim = get_bufpos (buf, limit, 0);
- limit_was_nil = 0;
- }
- CHECK_SYMBOL (prop, 1);
-
- XSETBUFFER (buffer, buf);
- extent = Fextent_at (make_number (bpos), buffer, prop, Qnil);
- if (!NILP (extent))
- value = Fextent_property (extent, prop);
- else
- value = Qnil;
-
- while (1)
- {
- bpos = XINT (Fnext_extent_change (make_number (bpos), buffer));
- if (bpos >= blim)
- break; /* property is the same all the way to the end */
- extent = Fextent_at (make_number (bpos), buffer, prop, Qnil);
- if ((NILP (extent) && !NILP (value)) ||
- (!NILP (extent) && !EQ (value, Fextent_property (extent, prop))))
- return make_number (bpos);
- }
-
- /* I think it's more sensible for this function to return nil always
- in this situation and it used to do it this way, but it's been changed
- for FSF compatibility. */
- if (limit_was_nil)
- return Qnil;
- else
- return make_number (blim);
- }
-
- /* See comment on previous function about why this is written in C. */
-
- DEFUN ("previous-single-property-change", Fprevious_single_property_change,
- Sprevious_single_property_change, 2, 4, 0,
- "Return the position of next property change for a specific property.\n\
- Scans characters backward from POS till it finds a change in the PROP\n\
- property, then returns the position of the change. The optional third\n\
- argument BUFFER is the buffer to scan (defaults to the current buffer).\n\
- The property values are compared with `eq'.\n\
- Return nil if the property is constant all the way to the start of BUFFER.\n\
- If the value is non-nil, it is a position less than POS, never equal.\n\n\
- If the optional fourth argument LIMIT is non-nil, don't search back\n\
- past position LIMIT; return LIMIT if nothing is found until LIMIT.\n\
- If two or more extents with conflicting non-nil values for PROP overlap\n\
- a particular character, it is undefined which value is considered to be\n\
- the value of PROP. (Note that this situation will not happen if you always\n\
- use the text-property primitives.)")
- (pos, prop, buffer, limit)
- Lisp_Object pos, prop, buffer, limit;
- {
- struct buffer *buf = decode_buffer (buffer, 0);
- Bufpos bpos = get_bufpos (buf, pos, 0);
- Bufpos blim;
- Lisp_Object extent, value;
- int limit_was_nil;
-
- if (NILP (limit))
- {
- blim = BUF_BEGV (buf);
- limit_was_nil = 1;
- }
- else
- {
- blim = get_bufpos (buf, limit, 0);
- limit_was_nil = 0;
- }
- CHECK_SYMBOL (prop, 1);
-
- XSETBUFFER (buffer, buf);
- /* extent-at refers to the character AFTER bpos, but we want the
- character before bpos. Thus the - 1. extent-at simply
- returns nil on bogus positions, so not to worry. */
- extent = Fextent_at (make_number (bpos - 1), buffer, prop, Qnil);
- if (!NILP (extent))
- value = Fextent_property (extent, prop);
- else
- value = Qnil;
-
- while (1)
- {
- bpos = XINT (Fprevious_extent_change (make_number (bpos), buffer));
- if (bpos <= blim)
- break; /* property is the same all the way to the beginning */
- extent = Fextent_at (make_number (bpos - 1), buffer, prop, Qnil);
- if ((NILP (extent) && !NILP (value)) ||
- (!NILP (extent) && !EQ (value, Fextent_property (extent, prop))))
- return make_number (bpos);
- }
-
- /* I think it's more sensible for this function to return nil always
- in this situation and it used to do it this way, but it's been changed
- for FSF compatibility. */
- if (limit_was_nil)
- return Qnil;
- else
- return make_number (blim);
- }
-
-
- /************************************************************************/
- /* initialization */
- /************************************************************************/
-
- void
- syms_of_extents (void)
- {
- defsymbol (&Qextentp, "extentp");
- defsymbol (&Qextent_replicap, "extent-replicap");
- defsymbol (&Qextent_live_p, "extent-live-p");
- defsymbol (&Qextent_replica_live_p, "extent-replica-live-p");
-
- defsymbol (&Qend_closed, "end-closed");
- defsymbol (&Qstart_open, "start-open");
- defsymbol (&Qall_extents_closed, "all-extents-closed");
- defsymbol (&Qall_extents_open, "all-extents-open");
- defsymbol (&Qall_extents_closed_open, "all-extents-closed-open");
- defsymbol (&Qall_extents_open_closed, "all-extents-open-closed");
- defsymbol (&Qstart_in_region, "start-in-region");
- defsymbol (&Qend_in_region, "end-in-region");
- defsymbol (&Qstart_and_end_in_region, "start-and-end-in-region");
- defsymbol (&Qstart_or_end_in_region, "start-or-end-in-region");
- defsymbol (&Qnegate_in_region, "negate-in-region");
-
- defsymbol (&Qdetached, "detached");
- defsymbol (&Qdestroyed, "destroyed");
- defsymbol (&Qbegin_glyph, "begin-glyph");
- defsymbol (&Qend_glyph, "end-glyph");
- defsymbol (&Qstart_open, "start-open");
- defsymbol (&Qend_open, "end-open");
- defsymbol (&Qstart_closed, "start-closed");
- defsymbol (&Qend_closed, "end-closed");
- defsymbol (&Qread_only, "read-only");
- /* defsymbol (&Qhighlight, "highlight"); in faces.c */
- defsymbol (&Qunique, "unique");
- defsymbol (&Qduplicable, "duplicable");
- defsymbol (&Qinvisible, "invisible");
- defsymbol (&Qintangible, "intangible");
- defsymbol (&Qdetachable, "detachable");
- defsymbol (&Qpriority, "priority");
-
- defsymbol (&Qglyph_layout, "glyph-layout"); /* backwards compatibility */
- defsymbol (&Qbegin_glyph_layout, "begin-glyph-layout");
- defsymbol (&Qbegin_glyph_layout, "end-glyph-layout");
- defsymbol (&Qoutside_margin, "outside-margin");
- defsymbol (&Qinside_margin, "inside-margin");
- defsymbol (&Qwhitespace, "whitespace");
- /* Qtext defined in general.c */
-
- defsymbol (&Qglyph_invisible, "glyph-invisible");
-
- defsymbol (&Qpaste_function, "paste-function");
- defsymbol (&Qcopy_function, "copy-function");
-
- defsymbol (&Qtext_prop, "text-prop");
- defsymbol (&Qtext_prop_extent_paste_function,
- "text-prop-extent-paste-function");
-
- defsymbol (&Qdup_list, "dup-list");
-
- defsubr (&Sextentp);
- defsubr (&Sextent_live_p);
- defsubr (&Sextent_detached_p);
- defsubr (&Sextent_start_position);
- defsubr (&Sextent_end_position);
- defsubr (&Sextent_object);
- defsubr (&Sextent_length);
- #if 0
- defsubr (&Sstack_of_extents);
- #endif
-
- defsubr (&Smake_extent);
- defsubr (&Scopy_extent);
- defsubr (&Sdelete_extent);
- defsubr (&Sdetach_extent);
- defsubr (&Sset_extent_endpoints);
- defsubr (&Snext_extent);
- defsubr (&Sprevious_extent);
- #if DEBUG_XEMACS
- defsubr (&Snext_e_extent);
- defsubr (&Sprevious_e_extent);
- #endif
- defsubr (&Snext_extent_change);
- defsubr (&Sprevious_extent_change);
-
- defsubr (&Sextent_parent);
- defsubr (&Sextent_children);
- defsubr (&Sset_extent_parent);
-
- defsubr (&Sextent_in_region_p);
- defsubr (&Smap_extents);
- defsubr (&Smap_extent_children);
- defsubr (&Sextent_at);
-
- defsubr (&Sset_extent_begin_glyph);
- defsubr (&Sset_extent_end_glyph);
- defsubr (&Sextent_begin_glyph);
- defsubr (&Sextent_end_glyph);
- defsubr (&Sset_extent_begin_glyph_layout);
- defsubr (&Sset_extent_end_glyph_layout);
- defsubr (&Sextent_begin_glyph_layout);
- defsubr (&Sextent_end_glyph_layout);
- defsubr (&Sset_extent_priority);
- defsubr (&Sextent_priority);
- defsubr (&Sset_extent_property);
- defsubr (&Sextent_property);
- defsubr (&Sextent_properties);
-
- defsubr (&Shighlight_extent);
- defsubr (&Sforce_highlight_extent);
-
- defsubr (&Sinsert_extent);
- defsubr (&Sstring_extent_data);
- defsubr (&Sset_string_extent_data);
- defsubr (&Smake_extent_replica);
- defsubr (&Sextent_replica_p);
- defsubr (&Sextent_replica_live_p);
- defsubr (&Sextent_replica_extent);
- defsubr (&Sextent_replica_start);
- defsubr (&Sextent_replica_end);
-
- defsubr (&Sput_text_property);
- defsubr (&Sput_nonduplicable_text_property);
- defsubr (&Sadd_text_properties);
- defsubr (&Sremove_text_properties);
- defsubr (&Stext_prop_extent_paste_function);
- defsubr (&Snext_single_property_change);
- defsubr (&Sprevious_single_property_change);
- }
-
- void
- vars_of_extents (void)
- {
- DEFVAR_INT ("mouse-highlight-priority", &mouse_highlight_priority,
- "The priority to use for the mouse-highlighting pseudo-extent\n\
- that is used to highlight extents with the `highlight' attribute set.\n\
- See `set-extent-priority'.");
- /* Set mouse-highlight-priority (which ends up being used both for the
- mouse-highlighting pseudo-extent and the primary selection extent)
- to a very high value because very few extents should override it.
- 1000 gives lots of room below it for different-prioritied extents.
- 10 doesn't. ediff, for example, likes to use priorities around 100.
- --ben */
- mouse_highlight_priority = /* 10 */ 1000;
-
- staticpro (&Vlast_highlighted_extent);
- Vlast_highlighted_extent = Qnil;
-
- extent_auxiliary_defaults.begin_glyph = Qnil;
- extent_auxiliary_defaults.end_glyph = Qnil;
- extent_auxiliary_defaults.parent = Qnil;
- extent_auxiliary_defaults.children = Qnil;
- extent_auxiliary_defaults.priority = 0;
-
- staticpro (&Vthis_is_a_dead_extent_replica);
- XSETEXTENT (Vthis_is_a_dead_extent_replica, make_extent ());
- }
-